Problems with strncpy and their elimination

I learn C and read through Learn C The Hard Way (ISBN-10: 0-321-88492-2). I am stuck in exercise 17, How to Break It.

Here is the problem from the book:

There is an error in this program since strncpy is poorly designed. Go read about strncpy, then try to figure out what happens when the name or address you specify is more than 512 bytes. Fix this by simply forcing the last character "\ 0" so that it always sets (which strncpy should do).

I read strncpy and understand that it is unsafe because it does not add a null byte to the end of the line. However, I do not know how to pass functions to a large number of bytes, and I am not sure how to fix a problem with a zero byte.

Below is a function using strncpy, MAX_DATA - 512.

 void Database_set(struct Connection *conn, int id, const char *name, const char *email) { struct Address *addr = &conn->db->rows[id]; if(addr->set) die("Already set, delete it first"); addr->set = 1; // WARNING: bug, read the "How To Break It" and fix this char *res = strncpy(addr->name, name, MAX_DATA); // demonstrate the strncpy bug if(!res) die("Name copy failed"); res = strncpy(addr->email, email, MAX_DATA); if(!res) die("Email copy failed"); } 

How to break it - CHANGE

The following is an example of how to break strncpy:

 void Database_set(struct Connection *conn, int id, const char *name, const char *email) { struct Address *addr = &conn->db->rows[id]; if(addr->set) die("Already set, delete it first"); addr->set = 1; // WARNING: bug, read the "How To Break It" and fix this char name2[] = { 'a', 's', 't', 'r', 'i', 'n', 'g' }; char *res = strncpy(addr->name, name2, MAX_DATA); // demonstrate the strncpy bug if(!res) die("Name copy failed"); res = strncpy(addr->email, email, MAX_DATA); if(!res) die("Email copy failed"); } 

To fix, add a null byte to the end of the line. change name2 as follows:

  char name2[] = { 'a', 's', 't', 'r', 'i', 'n', 'g', '\0' }; 

or, add the following line above the strncpy function call

 names2[sizeof(names2)-1] = '\0'; 
+3
source share
5 answers

Another way to fix strncpy error is to fix printf call

 void Address_print(struct Address *addr) { printf("%d %.*s %.*s\n", addr->id, sizeof(addr->name), addr->name, sizeof(addr->email), addr->email); } 

This limits printf to output no more than the entire array of characters, but no more.

+5
source

Why not just replace strncpy with strlcpy? According to the strlcpy man page:

  EXAMPLES The following sets chararray to ``abc\0\0\0'': (void)strncpy(chararray, "abc", 6); The following sets chararray to ``abcdef'' and does not NUL terminate chararray because the length of the source string is greater than or equal to the length parameter. strncpy() only NUL terminates the destination string when the length of the source string is less than the length parameter. (void)strncpy(chararray, "abcdefgh", 6); Note that strlcpy(3) is a better choice for this kind of operation. The equivalent using strlcpy(3) is simply: (void)strlcpy(buf, input, sizeof(buf)); The following copies as many characters from input to buf as will fit and NUL terminates the result. Because strncpy() does not guarantee to NUL terminate the string itself, it must be done by hand. char buf[BUFSIZ]; (void)strncpy(buf, input, sizeof(buf) - 1); buf[sizeof(buf) - 1] = '\0'; 
+3
source

You can do the following, assuming str1 and str2 are arrays of characters:

 strncpy(str1, str2, sizeof(str1) - 1); str1[sizeof(str1)-1] = '\0'; 

This will always contain the last character \0 , no matter how long str2 . But keep in mind that if str2 greater than str1 , then the string will be truncated.

+2
source

The man page for strncpy actually gives a sample code on how to fix this error:

 strncpy(buf, str, n); if (n > 0) buf[n - 1]= '\0'; 
+2
source

The main problem is that the name addr-> was not initialized, so this is a null pointer, it does not indicate where.

So, before you can use strncpy first, you need to allocate memory for the name addr-> name, otherwise it will not work.

And since this is a NULL pointer, NULL is returned if you did not set it, then the if statement will be true, and the die function will stop the program.

You can see the source code that the Database_create function does not initialize two line pointers from the structure address.

0
source

Source: https://habr.com/ru/post/1013498/


All Articles