-
Notifications
You must be signed in to change notification settings - Fork 241
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
lib/string/strdup/: STRNDUPA(): Reimplement in terms of strndupa(3) #1189
base: master
Are you sure you want to change the base?
Conversation
This comment was marked as outdated.
This comment was marked as outdated.
9a4b342
to
9227a34
Compare
Huh! Alpine has strdupa(3) but not strndupa(3). |
Hello @alejandro-colomar, I am not sure why you cc'd me on this PR... what puzzles me is the sheer amount of code for such simple tasks and the need for dubious and risky optimisations for utilities for which the only focus should be correctness and sturdiness, not efficiency. Best regards Chqrlie. |
Hi @chqrlie ,
I was curious about your opinion of
The reason to avoid strdup(3) is to make sure that a function can never fail. See #1188 (comment). I want to call STRDUPA() there. Adding error handling would mean that we need to decide what to do on error, which isn't necessarily an easy decision. Since strdupa(3) cannot fail, and should be safe for small string literals, it's actually easier to use than strdup(3) in this case. But yeah, we minimize the use of alloca(3). We have only 3 calls to STRNDUPA(), and I expect to only add a similar amount (or less) of STRDUPA() calls. BTW, we only use STRNDUPA() with input from utmp(5) members, which are 32-byte arrays or so. Best regards, |
Another approach would be to use a compound literal |
I thought it might be related to this post on StackOverflow (among others...) and indeed it should be enough for this context. It would not catch pathological cases such as
If there is a reason for Another advantage of
You could also use local arrays where you copy the string literals or compound literals whose lifetime is more precisely defined.
The whole Always happy to help. Chqrlie. |
Heh, yeah, I guess it's good enough. We should be able to catch those in code review. :) I eventually thought of implementing it as #define STRDUPA(s) STRNDUPA("" s "") which would make sure that the input is also an array, which would reject these pathological cases, by requiring an array.
Yeah, strdup(3) shouldn't fail either. But then, I would find it gross calling strdup(3) and not checking for errors, so I would probably add them anyway. And if we add the error handling, we still need to think on what to do in that case, and I don't think there's a good answer to that in this specific use case. Plus, actually crashing might be a good thing to do here. If I were to call strdup(3) here, I would probably just exit(1) on error. Plus, strdupa(3) makes it easier to justify why we add guarantees that the input is a string literal. With strdup(3), since we would have error handling anyway, we could just accept anything, which itself could prompt strdup(3) to actually fail.
Hmmm.
Actually, I recently had a discussion with a WG14 member, and he showed me some danger with compound literals passed as arguments, in combination with GNU's #define overwrite(s) \
({ \
strcpy(s, "bar"); \
})
puts(overwrite((char []){"foo"})); The lifetime of the CL has finished before puts(3) tries to read it, so UB. strdup(3) has a safer lifetime.
Yes, utmp(5) is deprecated. There's utmpx(5) and utmps, and those are not necessarily deprecated. And they're mostly similar, I think. But yeah, we had some changes recently, and expect we'll do more in the future. For now, I'm just cleaning up string and memory handling, to make code safer, without thinking too much on that.
Yep, we're quite aware of the lack of null termination with utmp(5). I did some extensive fixes there, and came up with a conclusion: For using those non-strings, always copy them into a proper string first, with $ grep -rn 'STRNDUPA('
src/logoutd.c:57: user = STRNDUPA(ut->ut_user);
src/logoutd.c:58: line = STRNDUPA(ut->ut_line);
src/logoutd.c:228: STRNDUPA(ut->ut_user),
lib/string/strdup/strndupa.h:19:#define STRNDUPA(s) \ $ grep -rn 'STRNDUP('
lib/string/strdup/xstrndup.h:19:#define XSTRNDUP(s) \
lib/utmp.c:192: *out = XSTRNDUP(ut->ut_host);
lib/utmp.c:261: hostname = XSTRNDUP(ut->ut_host); $ grep -rn 'STRNCAT('
src/logoutd.c:208: STRNCAT(tty_name, ut->ut_line);
lib/string/strcpy/strncat.h:16:#define STRNCAT(dst, src) strncat(dst, src, NITEMS(src))
lib/string/strdup/strndupa.h:21: STRNCAT(strcpy(alloca(strnlen(s, NITEMS(s)) + 1), ""), s) \
lib/string/strdup/xstrndup.h:21: STRNCAT(strcpy(XMALLOC(strnlen(s, NITEMS(s)) + 1, char), ""), s) \ We work with those strings, and eventually copy back into utmp(5) fields with STRNCPY(), which makes sure that the destination is an array (so we avoid passing bogus pointers to it). $ grep -rn 'STRNCPY(' lib
lib/string/strcpy/strncpy.h:16:#define STRNCPY(dst, src) strncpy(dst, src, NITEMS(dst))
lib/utmp.c:274: STRNCPY(utent->ut_line, line);
lib/utmp.c:276: STRNCPY(utent->ut_id, ut->ut_id);
lib/utmp.c:279: STRNCPY(utent->ut_id, line + 3);
lib/utmp.c:282: STRNCPY(utent->ut_name, name);
lib/utmp.c:284: STRNCPY(utent->ut_user, name);
lib/utmp.c:288: STRNCPY(utent->ut_host, hostname);
lib/log.c:86: STRNCPY(newlog.ll_host, host); Those 4 macros are the only string.h-based APIs allowed to interact with utmp(5) members in this project, and those APIs are not allowed to be used with anything other than utmp(5) in this project. These guidelines applied in review, plus the array checks to prevent accidents, seem to work quite well.
Thanks! :-)
Cheers, |
Note MUSL C does not have a strdupa function. I had to work around it with alloca & strcpy on the audit project. |
Hi! It seems it does have strdupa(3). What it doesn't have is strndupa(3) (which is a niche function for utmpx(5) members). alx@devuan:~/src/musl/libc/master$ grepc strdupa .
./include/string.h:#define strdupa(x) strcpy(alloca(strlen(x)+1),x)
alx@devuan:~/src/musl/libc/master$ grepc strndupa .
alx@devuan:~/src/musl/libc/master$ git blame -- include/string.h | grep strdupa
419ae6d5c (Rich Felker 2012-05-22 21:52:08 -0400 92) #define strdupa(x) strcpy(alloca(strlen(x)+1),x) |
musl doesn't provide strndupa(3). Signed-off-by: Alejandro Colomar <[email protected]>
Signed-off-by: Alejandro Colomar <[email protected]>
Cc: @chqrlie
Revisions:
v2
v2b
v3
v4
v4b
v4c
v5