Does memcpy really a copy of the memory? - c

I'm experimenting with memory handling in C.
Giving the following code
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef unsigned char BYTE;
typedef struct Data {
int valid;
double value;
} Data;
typedef struct Message {
int id;
int size;
int nr;
Data *data;
} Message;
int main() {
int sz = 5;
int id = 1;
int i;
Message msg;
msg.id = id;
msg.size = 0;
msg.nr = sz;
msg.data = malloc(sizeof(Data) * msg.nr);
for (i = 0; i < msg.nr; i++) {
msg.data[i].valid = 1;
msg.data[i].value = (double)i;
}
printf("Input data\nid: %d\nsize: %d\nnr: %d\n",
msg.id, msg.size, msg.nr);
for (i = 0; i < sz; i++)
printf("msg.data[%d].valid: %d\nmsg.data[%d].value: %lf\n",
i, msg.data[i].valid, i, msg.data[i].value);
int bufferSize = sizeof(msg) + (sizeof(Data) * msg.nr);
msg.size = bufferSize;
printf("bufferSize: %d\n", bufferSize);
BYTE *buffer = malloc(sizeof(BYTE) * bufferSize);
memcpy(buffer, &msg, bufferSize);
if (msg.data != NULL)
free(msg.data);
// test
Message *p = (Message *)buffer;
Message rcv;
rcv.id = 0;
rcv.size = 0;
rcv.nr = 0;
rcv.data = malloc(sizeof(Data) * p->nr);
memcpy(&rcv, buffer, p->size);
printf("Output data\nid: %d\nsize: %d\nnr: %d\n",
rcv.id, rcv.size, rcv.nr);
for (i = 0; i < sz; i++)
printf("rcv.data[%d].valid: %d\nrcv.data[%d].value: %lf\n",
i, rcv.data[i].valid, i, rcv.data[i].value);
if (rcv.data != NULL)
free(rcv.data);
if (buffer != NULL)
free(buffer);
}
I'm obtaining the following error at the end of the execution of the code
*** stack smashing detected ***: terminated
Going deeper what I found is that msg.data and rcv.data point to the same memory address
memory location
and when I free the rcv.data basically I'm releasing a memory location that as been released before.
I read that memcpy should create a copy, but I had a different experience.
I don't understand why it's happening.
I use gcc as compiler, and I tried to run the code in different machines, but I obtain the same result always.
Why this behavior?

Why the code contains undefined behaviors
The first call to memcpy reads starting from the address of msg for bufferSize bytes, where bufferSize is larger than the size of msg itself. Reading beyond the size of the object causes undefined behaviors.
int bufferSize = sizeof(msg) + (sizeof(Data) * msg.nr);
// This reads beyond the size of `msg`
memcpy(buffer, &msg, bufferSize);
The memcpy function copies the data byte-by-byte, including the Data* data field of Message, which is why you're getting the same address for both msg.data and rcv.data.
memcpy does not attempt to create a copy of the array pointed to by the data pointer. Copying bytes beyond the end of msg also doesn't help, because msg (which is on the stack) is not going to be adjacent with the array pointed to by msg.data (which is on the heap).
memcpy entry on cppreference
Solution for arbitrary length array
To fix this, call memcpy two times separately to serialize the Message and the array of Data.
int bufferSize = sizeof(Message) + sizeof(Data) * msg.nr;
BYTE* buffer = malloc(sizeof(BYTE) * bufferSize);
memcpy(buffer, &msg, sizeof(Message));
memcpy(buffer + sizeof(Message), msg.data, sizeof(Data) * msg.nr);
Similarly, on the receiver end, call memcpy two times to for the Message and the array of Data.
Message rcv;
memcpy(&rcv, buffer, sizeof(Message));
rcv.data = malloc(sizeof(Data) * rcv.nr);
memcpy(rcv.data, buffer + sizeof(Message), sizeof(Data) * rcv.nr);
Try it interactively on godbolt
Solution for size-limited array
If it's acceptable to have the data array to have a fixed maximum size NUM_DATA_MAX, then (de)serialization can be done with 1, instead of 2, calls to memcpy. To do so, define Message to contain an array data, instead of a pointer to an array.
#define NUM_DATA_MAX 10
typedef struct Data {
int valid;
double value;
} Data;
typedef struct Message {
int id;
int size;
int nr;
Data data[NUM_DATA_MAX];
} Message;
On the sender end
int bufferSize = sizeof(Message);
BYTE* buffer = malloc(bufferSize);
memcpy(buffer, &msg, sizeof(Message));
On the receiver end
Message rcv;
memcpy(&rcv, buffer, sizeof(Message));

Related

Passing buffer to function for writing

I'm trying to implement a quick prototype program to prepare a message that I intend to use as a tcp socket communication protocol.
I'm new at this, and I can't quite understand why running the following prints (null). Am I failing at passing the pointer to the subroutine?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
const char *header = "testhd";
void pack_message(char *body, char *buffer)
{
size_t body_size, msg_size, buffer_size;
char hex_size[11];
body_size = strlen(body);
snprintf(hex_size, 11, "0x%x", body_size);
msg_size = 16 + body_size;
buffer_size = msg_size + 1;
if (!buffer){
buffer = malloc(buffer_size);
}else{
buffer = realloc(buffer, buffer_size);
}
memset(buffer, 0, buffer_size);
strcat(buffer, header);
strcat(buffer, hex_size);
strcat(buffer, body);
}
int main(){
char *buffer = NULL;
char *body = "testmsg";
pack_message(body, buffer);
printf("%s", buffer);
return 0;
}
Please note that char *buffer in main function and char *buffer in pack_message function are two different pointers which are pointing to the same NULL address.
So in Your case functiom pack_message is working with local pointer and that's the reason why running Your code prints null.
To make Your code work You could potentially take an approach where You pass an address of pointer itself as an argument to the pack_message function so main function could look like that:
int main(){
char *buffer = NULL;
char *body = "testmsg";
pack_message(body, &buffer);
printf("%s", buffer);
return 0;
}
Your pack_message function should be changed to:
void pack_message(char *body, char **buffer)
{
size_t body_size, msg_size, buffer_size;
char hex_size[11];
body_size = strlen(body);
snprintf(hex_size, 11, "0x%zx", body_size);
msg_size = 16 + body_size;
buffer_size = msg_size + 1;
if (NULL == *buffer){
*buffer = malloc(buffer_size);
}else{
*buffer = realloc(*buffer, buffer_size);
}
memset(*buffer, 0, buffer_size);
strcat(*buffer, header);
strcat(*buffer, hex_size);
strcat(*buffer, body);
}
Please note that there I changed also one more potential issue:
snprintf(hex_size, 11, "0x%zx", body_size);
As body_size is of type size_t it is better idea to use %zx as size_t can be different size than unsigned integer, for details on printf please take a look at: https://cplusplus.com/reference/cstdio/printf/
Similar approach can be taken to char *body = "testmsg"; especially when its value will be much longer string.
For further improvements I would recommend to redesign pack_message function to return a pointer to allocated buffer as it will lower the chances to forget about freeing memory when not needed anymore.

C: Pipe: How to pass a dynamic int array using pipe correctly

I tried to write a dynamic allocated int array in several child processes, and read them in parent process using pipe, but the result is like [-15236548, 37526, -15236548, 37526,0,0,0,0,0,0], it looks like the addresses are read instead of the real values[1,1,1,1,0,2,1,1,1,1]. I am really struggling in it for the whole day, any help would be greatly appreciated!!!
The child process are 10, they would first write an array each, and after all of them are done, parent process reads the 10 array.
int *getArr(){
int * arr1 = malloc(sizeof(int) *100);
//do something
return arr1;
}
//write values to pipe
void write_check(int fd, void *buffer, size_t len){
char *p = buffer;
while(len>0){
size_t wlen = write(fd, p, len);
if(wlen <= 0)
exit(0);
p += wlen;
len -= wlen;
}
}
// read values from pipe
void read_check(int fd, void *buffer, size_t len){
char *p = buffer;
while(len>0){
size_t rlen = read(fd, p, len);
if(rlen <= 0)
exit(0);
p += rlen;
len -= rlen;
}
}
// pass values, and get it ready to be wrote
void write_ints(int fd, int *p, int len){
write_check(fd, p, len * sizeof(*p));
}
// allocate memory for fetching values
void read_ints(int fd, int *p, int len){
p = malloc(len * sizeof(*p));
read_check(fd, p, len* sizeof(*p));
}
int main(){
int len = 100;
int *arr1 = malloc(sizeof(int) * 100);
int *fds = malloc(sizeof(int) * 10 * 2);
// child processes write values to pipe
for(int i=0; i<10; i++){
child_pid = fork();
if (child_pid == 0){
// do something
arr1 = getArr();
write_ints(fd[i*2+1], &arr1, len);
}
}
// parent process reads values
for(int i=0; i<10; i++){
// do something
read_ints(fd[i*2], &arr1, len);
}
}
void read_ints(int fd, int *p, int len){
p = malloc(len * sizeof(*p));
read_check(fd, p, len* sizeof(*p));
}
This function is unusable. You pass it a pointer, which it ignores. It then allocates some memory, reads some integers into that memory, and does nothing with the pointer to that memory leaving the caller no way to access the read values.
If this function is going to allocate memory, it needs to return a pointer to the memory it allocated. If this function is going to receive a pointer to the memory it's going to read the integers into, then it should read the integers into the supplied buffer and not allocate its own.
Your code would be much easier to debug if it had comments. Looking at this function, I cannot tell what it's supposed to do and I see conflicting information in the code. Is it supposed to allocate a buffer to read integers into or is it supposed to be supplied a pointer to the buffer to read into? A comment would clear that up.
You cannot count on your code to document your intentions yet because your code isn't yet good enough to do that. So you need to use comments so that other people can tell what you meant to do, not just what you actually did.
expected int * but argument is int ** error is due to passing &arr1 .address of a pointer is a double pointer.just pass arr1 (in both write_ints and read_ints) and you wont get that error.

memory corruption when manipulating a long string

I am writing a program to print out any line input that is longer than 3.
It works for some fairly long input lines, but for the string that is too long, I got a error message of memory corruption
*** Error in `./print-80': malloc(): memory corruption (fast): 0x00000000022ff030 ***
I don't know where the error is from. Can anyone explain me why there is the error and how to fix it?
Below is the program
#include <stdio.h>
#include <stdlib.h>
#define LIMIT 3
#define LEAST_LENGTH 3
//function prototype
void copy(char* from, char* to);
int getline(char* s, int capacity);
int increase_capacity(char* s, int capacity);
int main(void)
{
int length, i;
char* line = calloc(LIMIT, sizeof(char));
while ((length = getline(line, LIMIT)) > 0)
{
if (length > LEAST_LENGTH)
printf("Output: %s\n", line);
//reset the line
for (i = 0; i < length; i++)
*(line + i) = 0;
}
free(line);
return 0;
}
int getline(char* line, int capacity)
{
int c, length;
length = 0;
while ((c = getchar()) != EOF && c != '\n')
{
if (length > (capacity - 1))
{
capacity = increase_capacity(line, capacity);
printf("Address of line after increasing cap: %p\n", line);
}
line[length++] = c;
}
if (c == '\n')
line[length++] = '\0';
return length;
}
int increase_capacity(char* s, int capacity)
{
int i;
capacity *= 2;
char *new_s = calloc(capacity, sizeof(char));
copy(s, new_s);
s = new_s;
free(new_s);
return capacity;
}
void copy(char* from, char* to)
{
int i = 0;
while ((to[i] = from[i]) != '\0')
++i;
}
Your increase_capacity function can change the address at which the data is stored. But it doesn't return this information to its caller. So getline will write to the old buffer address. Similarly, main has no way to get the new address, so it will access the old address and free a block that may already be freed.
Also, your increase_capacity function allocates memory to hold the data and then frees that memory. That leaves no place to hold the data!
int increase_capacity(char* s, int capacity)
{
int i;
capacity *= 2;
char *new_s = calloc(capacity, sizeof(char)); // allocate a larger block
copy(s, new_s); // copy the data into the larger block
s = new_s; // stash a pointer to the larger block in a local
free(new_s); // free the block?!
return capacity;
}
So we allocate a new block, copy the data into it, and then free it. That makes no sense, we need to keep the larger block since that's the whole point of a function to increase capacity. We also don't return the address of the new block, so even if we didn't free it, no other code could access it and we'd just wind up leaking it. Double oops.
I suggest you create a struct that holds both the pointer to the block and its size. Pass a pointer to that struct to functions like increase_capacity so it can modify the pointer and the size in the structure and callers can see the changes.

project struct to a array of bytes in c

Suppose I have array of chars:
char buffer[1024];
The array in fact contains chain of structures in sequence.
struct MyStruct {
char name[4];
int num1;
int num2;
}
I want to loop through the array:
MyStruct *p;
for(int i = 0;i < sizeof(buffer);i += sizeof(MyStruct))
{
// how can I point p to some place in buffer here?
}
I want to point p to start of buffer, the to buffer + 12 etc.
One issue to consider is that the char buffer might not be properly aligned for a struct (and, in this case, its int members num1 and num2). Depending on the platform and implementation, a 4-byte, 8-byte or 16-byte alignment might be required. For that reason, one alternative is to declare the buffer in terms of MyStruct initially and then access it via a char pointer:
MyStruct buffer[1024 / sizeof(MyStruct)];
char * cp = (char *) buffer;
// fill the buffer via cp
for (size_t i = 0; i < sizeof(buffer); ++i)
{
// do stuff with buffer[i]
}
If that approach is not possible, the buffer needs to be copied to another buffer with safe alignment; For example:
size_t n = sizeof(buffer) / sizeof(MyStruct);
MyStruct * p = (MyStruct *) malloc(n * sizeof(MyStruct));
if (!p) { exit(EXIT_FAILURE); }
memcpy(p, buffer, n * sizeof(MyStruct)); // copy buffer to p
for (size_t i = 0; i < n; ++i)
{
// do stuff with p[i]
}
first note that you are assuming that this will work. That there is no padding between the elements of the struct. Having said that do this:
MyStruct *s = (MyStruct*)(buffer + i)
You let p point to the first struct in the buffer, then increment it on each iteration:
MyStruct *p= (struct MyStruct *) buffer;
for(int i = 0; i < sizeof(buffer); i += sizeof(MyStruct), p++)
{
// your code
}
...and yes, this assumes the structs are contiguous in memory, with no padding in between.

Copying a file line by line into a char array with strncpy

So i am trying to read a text file line by line and save each line into a char array.
From my printout in the loop I can tell it is counting the lines and the number of characters per line properly but I am having problems with strncpy. When I try to print the data array it only displays 2 strange characters. I have never worked with strncpy so I feel my issue may have something to do with null-termination.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char* argv[])
{
FILE *f = fopen("/home/tgarvin/yes", "rb");
fseek(f, 0, SEEK_END);
long pos = ftell(f);
fseek(f, 0, SEEK_SET);
char *bytes = malloc(pos); fread(bytes, pos, 1, f);
int i = 0;
int counter = 0;
char* data[counter];
int length;
int len=strlen(data);
int start = 0;
int end = 0;
for(; i<pos; i++)
{
if(*(bytes+i)=='\n'){
end = i;
length=end-start;
data[counter]=(char*)malloc(sizeof(char)*(length)+1);
strncpy(data[counter], bytes+start, length);
printf("%d\n", counter);
printf("%d\n", length);
start=end+1;
counter=counter+1;
}
}
printf("%s\n", data);
return 0;
}
Your "data[]" array is declared as an array of pointers to characters of size 0. When you assign pointers to it there is no space for them. This could cause no end of trouble.
The simplest fix would be to make a pass over the array to determine the number of lines and then do something like "char **data = malloc(number_of_lines * sizeof(char *))". Then doing assignments of "data[counter]" will work.
You're right that strncpy() is a problem -- it won't '\0' terminate the string if it copies the maximum number of bytes. After the strncpy() add "data[counter][length ] = '\0';"
The printf() at the end is wrong. To print all the lines use "for (i = 0; i < counter; i++) printf("%s\n", data[counter]);"
Several instances of bad juju, the most pertinent one being:
int counter = 0;
char* data[counter];
You've just declared data as a variable-length array with zero elements. Despite their name, VLAs are not truly variable; you cannot change the length of the array after allocating it. So when you execute the lines
data[counter]=(char*)malloc(sizeof(char)*(length)+1);
strncpy(data[counter], bytes+start, length);
data[counter] is referring to memory you don't own, so you're invoking undefined behavior.
Since you don't know how many lines you're reading from the file beforehand, you need to create a structure that can be extended dynamically. Here's an example:
/**
* Initial allocation of data array (array of pointer to char)
*/
char **dataAlloc(size_t initialSize)
{
char **data= malloc(sizeof *data * initialSize);
return data;
}
/**
* Extend data array; each extension doubles the length
* of the array. If the extension succeeds, the function
* will return 1; if not, the function returns 0, and the
* values of data and length are unchanged.
*/
int dataExtend(char ***data, size_t *length)
{
int r = 0;
char **tmp = realloc(*data, sizeof *tmp * 2 * *length);
if (tmp)
{
*length= 2 * *length;
*data = tmp;
r = 1;
}
return r;
}
Then in your main program, you would declare data as
char **data;
with a separate variable to track the size:
size_t dataLength = SOME_INITIAL_SIZE_GREATER_THAN_0;
You would allocate the array as
data = dataAlloc(dataLength);
initially. Then in your loop, you would compare your counter against the current array size and extend the array when they compare equal, like so:
if (counter == dataLength)
{
if (!dataExtend(&data, &dataLength))
{
/* Could not extend data array; treat as a fatal error */
fprintf(stderr, "Could not extend data array; exiting\n");
exit(EXIT_FAILURE);
}
}
data[counter] = malloc(sizeof *data[counter] * length + 1);
if (data[counter])
{
strncpy(data[counter], bytes+start, length);
data[counter][length] = 0; // add the 0 terminator
}
else
{
/* malloc failed; treat as a fatal error */
fprintf(stderr, "Could not allocate memory for string; exiting\n");
exit(EXIT_FAILURE);
}
counter++;
You are trying to print data with a format specifier %s, while your data is a array of pointer s to char.
Now talking about copying a string with giving size:
As far as I like it, I would suggest you to use
strlcpy() instead of strncpy()
size_t strlcpy( char *dst, const char *src, size_t siz);
as strncpy wont terminate the string with NULL,
strlcpy() solves this issue.
strings copied by strlcpy are always NULL terminated.
Allocate proper memory to the variable data[counter]. In your case counter is set to 0. Hence it will give segmentation fault if you try to access data[1] etc.
Declaring a variable like data[counter] is a bad practice. Even if counter changes in the subsequent flow of the program it wont be useful to allocate memory to the array data.
Hence use a double char pointer as stated above.
You can use your existing loop to find the number of lines first.
The last printf is wrong. You will be printing just the first line with it.
Iterate over the loop once you fix the above issue.
Change
int counter = 0;
char* data[counter];
...
int len=strlen(data);
...
for(; i<pos; i++)
...
strncpy(data[counter], bytes+start, length);
...
to
int counter = 0;
#define MAX_DATA_LINES 1024
char* data[MAX_DATA_LINES]; //1
...
for(; i<pos && counter < MAX_DATA_LINES ; i++) //2
...
strncpy(data[counter], bytes+start, length);
...
//1: to prepare valid memory storage for pointers to lines (e.g. data[0] to data[MAX_DATA_LINES]). Without doing this, you may hit into 'segmentation fault' error, if you do not, you are lucky.
//2: Just to ensure that if the total number of lines in the file are < MAX_DATA_LINES. You do not run into 'segmentation fault' error, because the memory storage for pointer to line data[>MAX_DATA_LINES] is no more valid.
I think that this might be a quicker implementation as you won't have to copy the contents of all the strings from the bytes array to a secondary array. You will of course lose your '\n' characters though.
It also takes into account files that don't end with a new line character and as pos is defined as long the array index used for bytes[] and also the length should be long.
#include <stdio.h>
#include <stdlib.h>
#define DEFAULT_LINE_ARRAY_DIM 100
int main(int argc, char* argv[])
{
FILE *f = fopen("test.c", "rb");
fseek(f, 0, SEEK_END);
long pos = ftell(f);
fseek(f, 0, SEEK_SET);
char *bytes = malloc(pos+1); /* include an extra byte incase file isn't '\n' terminated */
fread(bytes, pos, 1, f);
if (bytes[pos-1]!='\n')
{
bytes[pos++] = '\n';
}
long i;
long length = 0;
int counter = 0;
size_t size=DEFAULT_LINE_ARRAY_DIM;
char** data=malloc(size*sizeof(char*));
data[0]=bytes;
for(i=0; i<pos; i++)
{
if (bytes[i]=='\n') {
bytes[i]='\0';
counter++;
if (counter>=size) {
size+=DEFAULT_LINE_ARRAY_DIM;
data=realloc(data,size*sizeof(char*));
if (data==NULL) {
fprintf(stderr,"Couldn't allocate enough memory!\n");
exit(1);
}
}
data[counter]=&bytes[i+1];
length = data[counter] - data[counter - 1] - 1;
printf("%d\n", counter);
printf("%ld\n", length);
}
}
for (i=0;i<counter;i++)
printf("%s\n", data[i]);
return 0;
}

Resources