5 ms·
What is the preferred solution here? I usually just use "memset" to zeroize the whole destination string, then tell "strncpy" that my destination is 1 byte shor
by sidewndr46 2y ago
What is the preferred solution here? I usually just use "memset" to zeroize the whole destination string, then tell "strncpy" that my destination is 1 byte shorter than what it really is.
The real issue I've ran into is that "strncpy" assumes the source is null-terminated.
- connicpu 2y agoC11 adds `strcpy_s` which takes (dest, destsz, src) and returns an errno_t which will report an error if the src string is longer than destsz, as silent truncation is often not a desirable behavior. It also assigns dest[0]='\0' on error so you don't get an unterminated garbage string.
- david2ndaccount 2y agoOnly msvc provides strcpy_s and they don’t conform to the standard. Other libcs don’t provide it. Ignore everything from Annex K and write your own wrappers around memcpy. You should always know the size of your buffers.
- david2ndaccount 2y agoUse memcpy and do the size check yourself beforehand (taking the appropriate action if it doesn’t fit). Avoid any function starting with str except for strlen. Prefer pointer+length instead of relying on nul-terminated strings.
- nrclark 2y agoYou mean strnlen.
- paulryanrogers 2y agostrlcpy?
- saagarjha 2y agomemccpy, then use the return value to terminate.
- pjmlp 2y agoUse a proper C string library like SDS. Or move up from the 1970's Bell Labs, adopt C++ with the respective compiler switches to have bounds checking enabled for operator[](). Better yet, use something else instead of one of those two, pick whatver is your fancy.
- Sesse__ 2y agoThe sanest solution is, surprisingly, snprintf(dst, sizeof(dst), "%s", src).
- kragen 2y agoplease don't fill your program with fifty zillion string buffers of arbitrarily chosen sizes and then try to separately pass the right size in seventy zillion string-processing function calls. your code will be hard to read, buggy, and probably insecure
- Sesse__ 2y agoI agree with that statement, but it has nothing to do with snprintf() versus e.g. strcpy_s(), where you have exactly the same requirement to pass the right size. (Separately, there's a discussion about how many bytes you are allowed to read from the _source_, but to fix that, you need something like the Linux kernel's strscpy(), which isn't really widely supported in userspace.)
- kragen 2y agoi agree
- 1over137 2y agostrlcpy() is my favourite, alas the GNU folks stubbornly refuse to embrace it, last I checked.
- jandrese 2y agostrlcpy is still braindamaged. The need to return the length of the source string for compatibility with old code means it suffers from the some of the same issues strncpy did.
- 1over137 2y agoSure, but strlcpy is better than strcpy and strncpy (for strings). I almost never see code that uses the return value of any of them. It is a simple refactoring to change strcpy/strncpy to strlcpy and, though it doesn't solve truncation issues, it's a solid improvement by eliminating memory overruns and lack of null termination. It was added to OpenBSD in 1998 and then in FreeBSD, Mac OS X, Solaris, IRIX, etc. but its adoption was hampered by glibc stubbornly refusing to add it (until 2023 apparently).
- jandrese 2y agoIt is frustrating because while it is better, it is still flawed in an easily fixed way. What I wish the standard library had: ssize_t strscpy(char* dst, const char* src, ssize_t dsize); Copies src into dst, stopping when it either reaches a \0 byte in src or on copying dsize - 1 bytes, whichever happens first. dst is then null terminated. If the copy is not truncated the strscpy returns the number of bytes copied. If the copy is truncated dsize is returned. Returns a negative value and sets errno if either dst or src is NULL or dsize is < 1.
- kragen 2y agothe strlcpy paper explains why strlcpy isn't designed the way you suggest: https://www.usenix.org/legacy/event/usenix99/full_papers/millert/millert.pdf https://www.usenix.org/legacy/event/usenix99/full_papers/mil... they actually started out with your design and then fixed it: > The return values started out as the number of characters copied, since this was trivial to get as a side effect of the copy or concatenation. We soon decided that a return value with the same semantics as snprintf()’s was a better choice since it gives the programmer the most flexibility with respect to truncation detection and recovery. basically they wanted to treat string truncation due to insufficient space as an error condition, so they designed the interface to make it easy to check (code from the paper, with syntax corrections): len = strlcpy(path, homedir, sizeof(path)); if (len >= sizeof(path)) return (ENAMETOOLONG); len = strlcat(path, "/", sizeof(path)); if (len >= sizeof(path)) return (ENAMETOOLONG); your proposal does permit such simple error checking for strscpy, although it is marginally less efficient: len = strscpy(path, homedir, sizeof(path)); if (len <= strlen(homedir)) return (ENAMETOOLONG); but i can't think of anything your corresponding strscat could return to permit a similarly simple check. is there anything?
- kragen 2y agofor general-purpose string handling in software where failure is an option, i like the qmail stralloc approach if (!addrparse(arg)) { err_syntax(); return; } flagbarf = bmfcheck(); seenmail = 1; if (!stralloc_copys(&rcptto,"")) die_nomem(); if (!stralloc_copys(&mailfrom,addr.s)) die_nomem(); if (!stralloc_0(&mailfrom)) die_nomem(); out("250 ok\r\n"); basically you have a struct with buffer-pointer, length, and capacity fields, like a golang slice, and you modify it with a small number of functions which reallocate the buffer if it isn't big enough. the ones you see here are stralloc_copys, which sets the buffer contents to the contents of a nul-terminated string, and stralloc_0, which appends a nul to the buffer. there are also functions for appending an arbitrary byte, for copying one stralloc to another, for copying counted strings into strallocs, and for concatenation, for determining whether one is a prefix of another, etc., but depending on the application, you may or may not need to implement these the whole stralloc library is 97 lines of k&r c, so reimplementing the part you need for a given program is pretty trivial. it's in the public domain for most programs, a disadvantage of the particular way that stralloc is implemented in qmail is that you have to check every single copy or concatenation operation for an out-of-memory error, as you see above. this makes your code a lot longer. many applications are better off just aborting inside the memory allocation function if they run out of memory; getting out-of-memory handling correct is very difficult, especially if you don't devote a massive amount of effort to testing out-of-memory conditions (because they won't occur often enough just by chance to test your error-handling path) (another disadvantage of the way stralloc is implemented is that you probably don't really want to use unsigned int for the two length fields on lp64 platforms) for some applications you might prefer just using strdup() (or xstrdup()) or non-owning string-view types (a pointer and a length, perhaps into an input file you've mapped into memory), or lisp-style symbol interning (plus some kind of buffer management probably). arena allocation, if you can afford it, makes dynamic memory allocation for strings a much more reasonable thing to do: no risk of a memory leak, fast allocation, instant deallocation. but again some applications do poorly with arena allocation but please don't fill your program with fifty zillion string buffers of arbitrarily chosen sizes and then try to separately pass the right size in seventy zillion string-processing function calls. your code will be hard to read, buggy, and probably insecure. factor string buffer length handling into a small part of your program so that most of your code never has to think about string buffer lengths