linux kernel convert char * to uint8_t[6] (read string to mac) - c

I need to convert a string "00:11:22:33:44:55" to an uint8_t[6] representing a mac.
I tried on my own, read somewhere char can be casted to uint8_t, but I'm kinda exhausted to try on my own. :(
Maybe there is a function in the kernel which does what I want.
If not, here is my code, what do I do wrong?
char * cleaned_mac =NULL;
char * extractMac(unsigned char * shared_user_buffer, size_t offset) {
char * buffer = kmalloc(17, GFP_KERNEL);
cleaned_mac = kmalloc(13, GFP_KERNEL);
int i = 0;
strncpy(buffer, shared_user_buffer + offset, 17);
printk("BUFFER [%s]\n", buffer);
while (*buffer && i < 12) {
if (isxdigit(*buffer)) {
printk("BUFFER [%c]\n", *buffer);
cleaned_mac[i] = *buffer;
printk("CLEANED BUFFER [%c]\n", *cleaned_mac);
i++;
}
++buffer;
}
cleaned_mac[12]=0x00;
printk("CLEANED BUFFER [%s]\n", cleaned_mac);
return cleaned_mac;
}
calling it like:
uint8_t * mac;
mac = extractMac(shared_user_buffer, strlen(tmq_server_prefix));
printk(KERN_DEBUG "MAC[%s]\n", mac);
printk(KERN_DEBUG "MAC[%02x:%02x:%02x:%02x:%02x:%02x]\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
so when I give "08:00:27:19:1f:02" in the function the result is:
Oct 13 17:41:28 client2 kernel: [ 1953.179271] CLEANED BUFFER [080027191f02]
Oct 13 17:41:28 client2 kernel: [ 1953.179273] MAC[080027191f02]
Oct 13 17:41:28 client2 kernel: [ 1953.179276] MAC[30:38:30:30:32:37]
So 08 became 30 and 38 ? Why is that?
Solution inspired from Dave (thank you):
uint8_t * cleaned_mac = NULL;
uint8_t * extractMac(unsigned char * shared_user_buffer, size_t offset) {
char *c;
char * buffer = kmalloc(17, GFP_KERNEL);
int p = 0;
const char * sep = ":";
cleaned_mac = kmalloc(ETH_ALEN * sizeof(uint8_t), GFP_KERNEL);
strncpy(buffer, shared_user_buffer + offset, 17);
while ((c = strsep(&buffer, sep))) {
cleaned_mac[p++] = simple_strtol(c, NULL, 16);
}
return cleaned_mac;
}
Usage then:
uint8_t * mac;
mac = extractMac(shared_user_buffer, strlen(tmq_server_prefix));
printk(KERN_DEBUG "---------------MAC [%02x:%02x:%02x:%02x:%02x:%02x]\n",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);

I can't decipher how your code is supposed to work, so I'll just write how I would do it:
char* macIn = "08:00:27:19:1f:02";
uint8_t macOut[6] = {0};
sscanf(macIn, "%2x:%2x:%2x:%2x:%2x:%2x", macOut, macOut+1, macOut+2, macOut+3, macOut+4, macOut+5);
printf("MAC IN: [%s]\n", macIn);
printf("MAC OUT (hex): [%02x:%02x:%02x:%02x:%02x:%02x]\n",
macOut[0], macOut[1], macOut[2], macOut[3], macOut[4], macOut[5]);
printf("MAC OUT (decimal): [%02d:%02d:%02d:%02d:%02d:%02d]\n",
macOut[0], macOut[1], macOut[2], macOut[3], macOut[4], macOut[5]);

Tokenize the string, and call strtol on each result
char *c;
int p = 0;
for(c=strtok(buffer, ",");c;c=strtok(NULL, ","))
mac[p++] = strtol(c, NULL, 16);

The %02x printf format interprets mac[0] as an integer, and prints it out as a string by converting it to two-digit hex.
Since mac[0] holds the ASCII character 0, whose ASCII code is 0x30, it's perfectly normal that you get the output you have.

You have to take every pair of characters, verify that the are really in the range '0'..'9', 'A'..'F' or 'a'..'f'. Then you take the first, map it to its "meaning" (0..15), multiply it with 16 and add the second one, mapped as well.

I've encoutered the same problem, finally solved with this simple code, it is in linux kernel.
char *mac_local = "e4:95:6e:4e:ee:6c";
for(i=0;i<6;i++)
buffAssco[i+START_POS] = simple_strtol(mac_local+3*i,NULL,16)&0xff;

The 11-year-old answer from Chriszuma is actually wrong.
Reading "%2x" to an uint8_t will result in some unintentional memory modification.
The right specifier to read hex value for uint8_t is "%hhx"
So the sscanf should be like this:
sscanf(macIn, "%2hhx:%2hhx:%2hhx:%2hhx:%2hhx:%2hhx", macOut, macOut+1, macOut+2, macOut+3, macOut+4, macOut+5);

Related

kstrtoint in sysfs for c kernel module

Hi I am trying to use a kobject to write to a int array from sysfs. So the input is a char* and a size variable. I cant seem to get this to work however. My expected input is "num1 num2 num3 "
static ssize_t pids_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count) {
int num_count = 0;
int i = 0;
int result = 0;
int cur_pid = 0;
char *dst;
char *ddst;
printk(KERN_INFO "GPIO_DEGUG: enter");
dst = buf;
ddst = buf;
printk(KERN_INFO "GPIO_DEGUG: size of buffer %d ",count);
while(ddst < (buf + sizeof(size_t)*count)) {
ddst ++;
if (ddst[0] == ' ') {
result = kstrtoint(dst,10,&cur_pid);
dst=ddst+1;
printk(KERN_INFO "GPIO_DEGUG: kstrtoint suceeded %d ",cur_pid);
printk(KERN_INFO "GPIO_DEGUG: kstrtoint suceeded res: %d ",result);
pids[num_count] = cur_pid;
num_count += 1;
}
}
for(i=0;i<num_count;i++) {
printk(KERN_INFO "GPIO_TEST: pid: %d \n", pids[i]);
}
printk(KERN_INFO "GPIO_DEBUG: leaving\n");
return count;
}
When I echo "100 " > /sys/vt/vt7/pids I get
[ 2765.712770] GPIO_DEGUG: enter
[ 2765.724468] GPIO_DEGUG: size of buffer 5
[ 2765.735101] GPIO_DEGUG: kstrtoint suceeded 0
[ 2765.746526] GPIO_DEGUG: kstrtoint suceeded res: -22
[ 2765.757746] GPIO_DEBUG: leaving
I suppose this is an argument error any help would be nice, thanks.
Function kstrtoint expects full string to contain single integer value. The only exception is a newline character at the end of the string:
The string must be null-terminated, and may also include a single newline before its terminating null.
As you can see, string "100 " doesn't fit for that requirement: it contains exceeded space.
For parse only part of the string as an integer, you may use simple_strtol:
long val = simple_strtol(dst, &ddst, 10);
if(ddst == ddst) {/* Parsing has been failed. */};
While this function is marked as an obsolete, there is still some code in the kernel which uses it.
Another possibility is to use sscanf. It expects fixed number of integers in the string, but it is an usual situation with attributes: having complex representation of the attributes is not recommended:
The conventions for sysfs state that each attribute should contain a single, human-readable value; if you have a lot of information to return, you may want to consider splitting it into multiple attributes.
(Linux Device Drivers 3, chapter 14).
The kstrtoint function is defined here:
http://lxr.free-electrons.com/source/lib/kstrtox.c#L245
If you notice the last value of *res defined in the function is the value you wish to use. In your case cur_pid should be the value in which you want to print, the result should always be zero if it was successful. I would suggest checking result to make sure that the conversion has succeeded.
This should work:
int cur_pid, result;
char *dst = NULL;
cur_pid = result = 0;
dst = buf;
result = kstrtoint(dst, 10, &cur_pid);
if (result)
printk(KERN_INFO "GPIO_DEGUG: kstrtoint suceeded res: %d ", cur_pid);
else
printk(KERN_INFO "ERROR");

Converting hexadecimal to ASCII in c

I'm working on an Android rom for a mobile phone and I want to make the kernel load the wifi MAC address from the device's NV partition. My code looks like this:
#include <linux/kernel.h>
#include <linux/random.h>
#include <linux/syscalls.h>
#define ETHER_ADDR_LEN 6
#define FILE_WIFI_MACADDR "/dev/block/mmcblk0p7"
static int bcm_wifi_get_mac_addr(unsigned char *buf)
{
int ret = 0;
mm_segment_t oldfs;
int i;
int fp;
int macbyte;
int readlen = 0;
uint rand_mac;
static unsigned char mymac[ETHER_ADDR_LEN] = {0,};
const unsigned char nullmac[ETHER_ADDR_LEN] = {0,};
const unsigned char bcastmac[] = {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};
if (buf == NULL)
return -EAGAIN;
memset(buf, 0x00, ETHER_ADDR_LEN);
oldfs = get_fs();
set_fs(get_ds());
fp = sys_open(FILE_WIFI_MACADDR, O_RDONLY, 0);
if (fp < 0) {
pr_err("%s: Failed to read error %d for %s\n",
__FUNCTION__, fp, FILE_WIFI_MACADDR);
goto random_mac;
}
for (i = 0; i<12; i++) {
macbyte=0;
sys_lseek( fp,i+7680,SEEK_SET);
readlen = sys_read(fp,&macbyte,1);
if (i)
sprintf(macaddr,"%s%c",macaddr,macbyte);
else
sprintf(macaddr,"%c",macbyte);
}
if (readlen > 0) {
unsigned char* macbin;
macbin = (unsigned char*)macaddr;
pr_info("%s: READ MAC ADDRESS %02X:%02X:%02X:%02X:%02X:%02X\n",
__FUNCTION__,
macbin[0], macbin[1], macbin[2],
macbin[3], macbin[4], macbin[5]);
if (memcmp(macbin, nullmac, ETHER_ADDR_LEN) == 0 ||
memcmp(macbin, bcastmac, ETHER_ADDR_LEN) == 0) {
sys_close(fp);
goto random_mac;
}
memcpy(buf, macbin, ETHER_ADDR_LEN);
} else {
sys_close(fp);
goto random_mac;
}
sys_close(fp);
return ret;
random_mac:
set_fs(oldfs);
pr_debug("%s: %p\n", __func__, buf);
if (memcmp( mymac, nullmac, ETHER_ADDR_LEN) != 0) {
/* Mac displayed from UI is never updated..
So, mac obtained on initial time is used */
memcpy(buf, mymac, ETHER_ADDR_LEN);
return 0;
}
srandom32((uint)jiffies);
rand_mac = random32();
buf[0] = 0x00;
buf[1] = 0x90;
buf[2] = 0x4c;
buf[3] = (unsigned char)rand_mac;
buf[4] = (unsigned char)(rand_mac >> 8);
buf[5] = (unsigned char)(rand_mac >> 16);
memcpy(mymac, buf, 6);
pr_info("[%s] Exiting. MAC %02X:%02X:%02X:%02X:%02X:%02X\n",
__FUNCTION__,
buf[0], buf[1], buf[2], buf[3], buf[4], buf[5] );
return 0;
}
The idea is to load the nv parition, located at /dev/block/mmcblk0p7, then read the mac address, which is located at offset 7680 on the nv. The problem is that the MAC address is written in hex, so I'm trying to print it to an ASCII string using sprintf().
for (i = 0; i<12; i++) {
macbyte=0;
sys_lseek( fp,i+7680,SEEK_SET);
readlen = sys_read(fp,&macbyte,1);
if (i)
sprintf(macaddr,"%s%c",macaddr,macbyte);
else
sprintf(macaddr,"%c",macbyte);
}
In the nv the MAC looks something like this: 34 30 42 30 46 41 36 35 39 33 34 39, which in ASCII is 40B0FA659349. But instead the resulting MAC is 34:30:42:30:46:41, which tells me that the hex values are not getting converted at all.
What would be the proper way to export the hex values into an ASCII string? I'm new to programming and i was hoping someone could give me some tips.
Thanks in advance.
In your loop you are reading single bytes and converting them to hex strings, while what you actually need to do is read the hex string and convert it byte values. Unless you actually want a hex string, in which case no conversion is necessary.
You have 12 hex characters representing 6 bytes so:
#define MAC_LEN 6
uint8_t macbytes[MAC_LEN] ;
for( i = 0; i < MAC_LEN; i++ )
{
char hex_str[3] ;
unsigned byte_val ;
sys_lseek( fp, (i*2) + 7680, SEEK_SET ) ;
readlen = sys_read( fp, hex_str, 2 ) ;
sscanf( hex_str, "%2X", &byte_val ) ;
macbytes[i] = (uint8_t)byte_val ) ;
}
The data in NV is already ASCII coded hexadecimal; for example 0x34 is the ASCII code for the hex digit '4', and 0x30 that for '0', together the ASCII character pair "40" represent the single 8 bit integer value 0x40. So the conversion you need is ASCII to byte array, not "hex to ASCII" (which makes no semantic sense).
I think this is OP's stubbing block: forming a string version of the MAC address.
I'll make this wiki for anyone to modify, borrow or steal.
sys_lseek( fp,i+7680,SEEK_SET);
char macaddr[100];
char *p = macaddr;
const char *sep = "";
for (i = 0; i < 12; i++) {
unsigned char macbyte;
int readlen = sys_read(fp, &macbyte, 1);
if (readlen != 1) Handle_Error();
p += sprintf(p, "%s%02X", sep, macbyte);
sep = ":";
}
puts(macaddr);

C - Problems extracting data from buffer. Possibly endianess-related

I'm having some difficulties extracting data from a buffer using memcpy.
First, I memcpy some variables into a buffer:
int l1_connect(const char* hostname, int port) {
// Variables to be stored in the buffer
char *msg = "Hi, I'm a message"; // strlen(msg) == 17
uint16_t sender_id = htons(1); // sizeof(sender_id) == 2
uint16_t packet_size = htons(sizeof(packet_size)+sizeof(sender_id)+strlen(msg)); // sizeof(packet_size) == 2
// Checking values
printf("l1_connect():\nsender_id: %d, packet_size: %d\n\n", ntohs(sender_id), ntohs(packet_size));
// sender_id == 1, packet_size == 21
// The buffer
char buf[100];
// Copying everything
memcpy(&buf, &sender_id, sizeof(sender_id));
memcpy(&buf+sizeof(sender_id), &packet_size, sizeof(packet_size));
memcpy(&buf+sizeof(sender_id)+sizeof(packet_size), &msg, strlen(msg));
// Passing buf to another function
int bytes_sent = l1_send(1, buf, sizeof(buf));
}
I then try to extract that data (checking, before sending over UDP socket):
int l1_send( int device, const char* buf, int length ) {
// Variables in which to store extracted data
uint16_t id = 0;
uint16_t size = 0;
char msg[50];
memcpy(&id, &buf, sizeof(id));
memcpy(&size, &buf+sizeof(id), sizeof(size));
int remaining = ntohs(size) - (sizeof(id) + sizeof(size));
printf("l1_send():\nremaining: %d\n", remaining); // -37041
// memcpy-ing with correct(?) offset
memcpy(&msg, &buf+sizeof(id)+sizeof(size), 50);
msg[49] = '\0';
printf("id: %d\n", ntohs(id)); // 8372
printf("size: %d\n", ntohs(size)); // 37045
printf("msg: %s\n", msg); // ��$_�
return 0; // For now
}
As you can see, the values aren't quite what I'm expecting. Can anyone tell me what I'm doing wrong?
Your pointer math is incorrect. You're using &buf where you should just be using buf. If this doesn't explain what is wrong, nothing else I can say will:
#include <stdio.h>
int main(int argc, char **argv)
{
char buff[100];
printf("buff : %p\nbuff+10 : %p\n&buff+10 : %p\n", buff, buff+10, &buff+10);
return 0;
}
Output (varies by platform, obviously)
buff : 0xbf87a8bc
buff+10 : 0xbf87a8c6
&buff+10 : 0xbf87aca4
See it live. The math you're doing is incrementing by type, which for &buf is a pointer to array of 100 chars; not a simple char address. Therefore, &buff + 10 (in my sample) says "give me the 10th array of 100 chars from where I am now.". The subsequent write is invoking undefined behavior as a consequence.
Valgrind is your buddy here, btw. It would have caught this in a heartbeat.
Update
May as well fill in the entire gambit while I'm here. This is also wrong in l1_send:
memcpy(&id, &buf, sizeof(id));
// this------^
and the subsequent other areas you're using it in that function. You're taking the address of a parameter pointer, not the value within it. I'm confident you need buf there as well.
Try this:
memcpy(buf, &sender_id, sizeof(sender_id));
memcpy(buf + sizeof(sender_id), &packet_size, sizeof(packet_size));
memcpy(buf + sizeof(sender_id) + sizeof(packet_size), msg, strlen(msg));
To help you understand what is wrong with your code, you can read this.
Related: Pointer math vs. Array index

Message to buffer in C

I have a college project where need to convert an int to a buffer of char.
I need to use memcpy but when I copy the values it's not work because the msg_buf still empty.
I have some constraints:
- I need to use memcpy because my teacher will test my code like memcmp(msg_str, &opcode, 2) == 0).
Here is my code:
int message_to_buffer(struct message_t *msg, char **msg_buf){
int opcode = htons(msg->opcode);
int c_type = htons(msg->c_type);
int result;
int buffer = sizeof(opcode) + sizeof(c_type);
switch(msg->c_type){
case CT_RESULT:
result = htonl(msg->content.result);
buffer += sizeof(result);
*msg_buf = (char*)malloc(sizeof(char) * 12);
if(msg_buf == NULL)
return -1;
memcpy(*msg_buf,&opcode,sizeof(opcode));
break;
};
return buffer;
}
What is wrong here?
More specifically, you need to be copying the shorts as shorts, not ints. sizeof(short) != sizeof(int) (usually, depending on the architecture):
int message_to_buffer(struct message_t *msg, char **msg_buf){
short opcode = htons(msg->opcode);
short c_type = htons(msg->c_type);
int result;
char* buffer = NULL, *buf_start=NULL;
*msg_buf = NULL;
switch(msg->c_type){
case CT_RESULT:
result = htonl(msg->content.result);
buffer = (char*)malloc(sizeof(char) * 12);
if (buffer == NULL)
return -1;
buf_start = buffer;
memcpy(buffer,&opcode,sizeof(opcode)); // sizeof(short) == 2; sizeof(int) == 4
buffer += sizeof(opcode);
memcpy(buffer,&c_type,sizeof(c_type)); // sizeof(short) == 2; sizeof(int) == 4
buffer += sizeof(c_type);
memcpy(buffer,&result, sizeof(result));
buffer += sizeof(result);
*msg_buf = buf_start;
break;
};
return buffer - buf_start;
}
I think your problem may be that you are calling htons() on an int. htons() is meant to be used with values of type short, so you may be losing the upper 16 bits of your msg->opcode and msg->c_type there. Try replacing htons() with htonl() instead.
Also, it looks like you are allocating a 12-byte buffer with malloc(), but only writing 4 bytes into it, leaving the latter 8 bytes of it uninitialized/undefined. Is that intentional?
Why don't you use itoa function to convert int to char*? So you replace your memcpy with itoa function.
Reference: http://www.cplusplus.com/reference/cstdlib/itoa/
[EDIT]
If your compiler does not support itoa, you can use sprintf instead.

Issues with C in realloc() or free() or double free or corruption

I am trying to capture audio packets of variable size, strip the RTP header, then concatenate the audio data to 20 bytes each. My goal is to create something like a queue and just use pointer arithmetic to chop up data before I copy 20 bytes to a buffer. The issue occurs when I get a large amount of audio bytes coming into the queue(probably greater than 20). Here is the while loop that captures, copies to queue, and chops up the data:
run_flag = TRUE;
unsigned char *qs_ptr = NULL; //the very start of the queue
unsigned char *qcur_ptr = NULL; //the start of current audio packet
unsigned char *qsa_ptr = NULL; //the start of new incoming audio data
unsigned char *tmp_ptr = NULL; //points to the start of next audio packet to send
unsigned char audio_buf[20];
unsigned char buf[MAX_PACKET_LEN];
unsigned char *pkt_no_hdr = NULL;
int num_audio_bytes;
int tot_bytes;
int num_in_q;
/* listen for voip packets */
/* collection */
/* keeps track of audio bytes, send data when = 20 */
pf=fopen("rtp.dat","w");
while (run_flag==TRUE) {
if ((num_bytes = read(fd, buf, MAX_PACKET_LEN)) < 0) {
perror("recv");
close(sd);
exit(1);
}
pkt_no_hdr = (unsigned char *)calloc(num_bytes-12, sizeof(unsigned char));
/* remove 12 rtp header bytes */
num_audio_bytes = rem_rtp_hdr(pkt_no_hdr, &buf, num_bytes);
print_bytes(pkt_no_hdr, num_bytes-12);
printf("num audio bytes: %d\n", num_bytes-12);
tot_bytes+=num_audio_bytes;
num_in_q+=num_audio_bytes;
printf("num_in_q: %d\n", num_in_q);
cpy_to_q(&qs_ptr, &qcur_ptr, &qsa_ptr, pkt_no_hdr, num_audio_bytes, tot_bytes);
free(pkt_no_hdr);
if(num_in_q >= 20) {
tmp_ptr = qcur_ptr + 20;
memcpy(audio_buf, qcur_ptr, 20);
qcur_ptr = tmp_ptr;
print_bytes(audio_buf, 20);
// add header
// send mcast packet
num_in_q -= 20;
}
}
Here's the cpy_to_q function:
void cpy_to_q(unsigned char **qs_ptr, unsigned char **qcur_ptr, unsigned char **qsa_ptr, unsigned char *data, int num_bytes, int tot_bytes) {
if(*qs_ptr == NULL) {
*qs_ptr = (unsigned char *)malloc(num_bytes*sizeof(unsigned char) + 1);
*qcur_ptr = *qs_ptr;
*qsa_ptr = *qs_ptr;
memcpy(*qs_ptr, data, num_bytes);
} else {
*qs_ptr = (unsigned char *)realloc(*qs_ptr, tot_bytes*sizeof(unsigned char) + 1);
printf("size of q: %d\n", tot_bytes);
*qsa_ptr += num_bytes;
memcpy(*qsa_ptr, data, num_bytes);
}
}
I keep getting errors related to realloc() or free() which must happen in the cpy_to_q function:
\*** glibc detected \*** ./voipBridge: free(): invalid next size (fast): 0x000000000213b5b0 \***
Here is what valgrind says when the issue occurs:
Thread 1: status = VgTs_Runnable
==3799== at 0x4C2B4F0: realloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==3799== by 0x401211: cpy_to_q (handle_q.c:21)
==3799== by 0x40177A: rcv_enter code herertp (net_interface.c:142)
==3799== by 0x401D89: main (voip_bridge.c:48)
Here is what the log says before the issue:
num audio bytes: 6 n
num_in_q: 14
REALLOC
size of q: 94
bytes: 0bd2d4b5da0f
num audio bytes: 6
num_in_q: 20
REALLOC
size of q: 100
bytes: b15c0f0b86f3b15a0f0bd2d4b5da0f0000000000
bytes: 08cb24ad9a0f
num audio bytes: 6
num_in_q: 6
REALLOC
size of q: 106
bytes: 22c6a0d000e3980ba0f27ccca4336ef243e3168e57150fd6e388b8c7bf
num audio bytes: 29
num_in_q: 35
REALLOC
size of q: 135
*** glibc detected *** ./voipBridge: double free or corruption (out): 0x00000000023432f0 ***
Also, I realize the queue will just keep getting larger and larger. Is there a way to free up memory without freeing the entire block?
Thanks.
This is a significant problem:
void cpy_to_q(unsigned char **qs_ptr, unsigned char **qcur_ptr, unsigned char **qsa_ptr,
unsigned char *data, int num_bytes, int tot_bytes) {
if(*qs_ptr == NULL) {
*qs_ptr = (unsigned char *)malloc(num_bytes*sizeof(unsigned char) + 1);
*qcur_ptr = *qs_ptr;
*qsa_ptr = *qs_ptr;
memcpy(*qs_ptr, data, num_bytes);
} else {
// HERE YOU REALLOC THE BASE PTR, BUT DON"T REPOS THE CUR PTR
*qs_ptr = (unsigned char *)realloc(*qs_ptr, tot_bytes*sizeof(unsigned char) + 1);
printf("size of q: %d\n", tot_bytes);
*qsa_ptr += num_bytes;
memcpy(*qsa_ptr, data, num_bytes);
}
}
When the allocation finally becomes significant enough to warrant a new full page alloc, realloc() will work, but now you have a current-pointer still pointing to an old queue that doesn't even exist anymore.
To fix this, keep the delta from the current queue in a tmp size var, then base the new cur_ptr off the new queue-base after the realloc. The same housekeeping logic is needed for the sa ptr, btw.
So something like this, and note this assumes your list always grows and has an entirely separate reset() or shrink() mechanism.
void cpy_to_q
(
unsigned char **qs_ptr,
unsigned char **qcur_ptr,
unsigned char **qsa_ptr,
unsigned char *data,
int num_bytes,
int tot_bytes
)
{
if(*qs_ptr == NULL)
{
*qs_ptr = malloc(num_bytes*sizeof(unsigned char) + 1);
*qcur_ptr = *qs_ptr;
*qsa_ptr = *qs_ptr;
memcpy(*qs_ptr, data, num_bytes);
}
else
{
size_t cur_diff = *qcur_ptr - *qs_ptr;
size_t sa_diff = *qsa_ptr - *qs_ptr;
// now realloc (note: you really should error check this)
*qs_ptr = realloc(*qs_ptr, tot_bytes*sizeof(unsigned char) + 1);
printf("size of q: %d\n", tot_bytes);
// now reposition your old pointers.
*qcur_ptr = *qs_ptr + cur_diff;
*qsa_ptr = *qs_ptr + sa_diff;
// and finally continue as before
*qsa_ptr += num_bytes;
memcpy(*qsa_ptr, data, num_bytes);
}
}

Resources