6 ms·
Ah, there's some classic Drepper. It's so easy when you get to vet every line of source and never have to worry about inexperienced or careless programmers. Pe
by sitharus 13y ago
Ah, there's some classic Drepper. It's so easy when you get to vet every line of source and never have to worry about inexperienced or careless programmers.
Personally I much prefer APIs that will fail safe until you know you need the performance of a less safe one, and can spend the time auditing the code.
- saurik 13y agoThese APIs do not "fail safe"; as Kaz Kylheku points out in his reply to Ulrich on that list: > These words make sense. The problem with strlcat and strlcpy is that they assume that it's okay to arbitrarily discard data for the sake of preventing a buffer overflow. The buffer overflow may be prevented, but because data may have been discarded, the program is still incorrect. This is roughly analogous to clamping floating point overflow to DBL_MAX and merrily continuing in the calculation. ;) This is still a bug in your program, and it can still specifically be a security risk; if you want to cap a buffer, you need to fail the operation, not just silently discard some of the data. Further down-thread, here is Ulrich's (I will argue 100% correct) rant: > Dammit, it is not safe. It hides bugs in programs. If a string is too long for an allocated memory block the copying must not simply silently stop. Instead the program must reallocate or signal an error. I can construct you cases where the use of these stupid functions is creating new security problem. I realize it is fun (and sadly part of the zeitgeist at this point) to rag on Ulrich, but a lot of the things he's said over the years have been quite insightful, and you should at least read the full conversation before judging him.
- anon4 13y agoIsn't the point that strlcpy/cat are a better alternative to strncpy/cat, since the latter don't let you know at all that you didn't copy the entire string? And of course, past simple use cases, one would write one's own string library based on strlcpy/cat (for whatever cases in which it's not ok to include string length in the string struct).
- tobiasu 13y agostrl* fail just like any other C function in libc. They return, like snprintf(), the total length of the string and let the programmer deal with it. strlcpy(buf, s, sizeof(buf)) >= sizeof(buf) Rocket science. Ulrichs reputation is well deserved, he worked very hard on it.
- saurik 13y agoIf you are going to do that check, there is almost no value to using this function as you can do a similar check before copying the string. (The only benefit you get is that if you don't know the length of the source string you save having to first calculate it; but that's a performance optimization, not a fundamentally different paradigm.) The thing is, though: no one actually checks that return value, as this idea that using this API is what makes things secure has become zeitgeist. This post from OpenBSD is even making this mistake: they care only that people are using the API, not that they are using it correctly (despite pointing out the truncation issue at the end, they are just analyzing the usage, not the correctness). Just using this API is not a magic bullet: if you are using the API incorrectly, your program is still incorrect, and if you are using it correctly you can just use strcpy correctly. If you don't believe me, this is a really depressing search to perform: site:opensource.apple.com "strlcpy" -"strlcpy.c" -"strlcpy.s" -"strlcpy.3" -"strlcpy.h" OpenSSH doesn't even check strlcpy correctly... 317 /* No MIN_SIZEOF here - we absolutely *must not* truncate the 318 * username (XXX - so check for trunc!) */ 319 strlcpy(li->username, pw->pw_name, sizeof(li->username)); Yeah, that's great: we "absolutely must not" do something, but it's marked as a XXX?!?!?
- tobiasu 13y agoIf the world wrote perfect code, nobody would have ever needed to replace strcpy. Water is wet. Yet somehow, even you admit that they have some use because "almost no value" is infinitely more value than "no value". The benefit you describe is exactly what's beneficial. strlcpy does the length calculation, and prevents overflowing the buffer, which is a reasonable improvement over calling strcpy and trusting that previous checks (if they exist at all) are correct. Truncation (unless intended) can never occur when the program is correct, it merely exchanges one vulnerability for another one, which rarely can be exploited. That noone checks for the return value is trivially false. That Theo only cares about usage and not about correct usage is also false. Just because something isn't in a mail doesn't mean you can come to the reverse conclusion and claim the person doesn't care about a topic not mentioned. Most people tackle bigger problems first, before getting into discussions about writing "perfect" code. Given you are a perfect programmer (I assume), why don't you check the openssh code you quoted and let us know how you would've handled it? I looked at it, it's part of (portable) utmp logging and will truncate the username from passwd(5) to fit into the utmp structure. Nothing much can be done about it, because the two structures do not agree on common size limits. Demanding perfection while being unwilling to improve the status quo is hypocritical.
- InclinedPlane 13y agoLet's see: Scenario 1: a bug in your program leads to incorrect program behavior because strlcpy just did what it was told and the client code didn't detect the error. Scenario 2: a bug in your program leads to a buffer overflow vulnerability which can potentially lead to remote code execution. I think I know which set of problems I'd rather have.
- saurik 13y agoYou are making the assumption that scenario one does not lead to an exploitable vulnerability... the whole point of this complaint is that randomly truncating strings because they don't fit in buffers because you believe "strlcpy is magically better than strcpy" actually causes a different class of potentially-exploitable vulnerability... it doesn't, as the person I responded to claimed, "fail safe".
- __david__ 13y agoYes, it might lead to an exploitable bug, but it seems to me that it's a much lower probability than a buffer overflow leads to an exploitable bug. Surely an API that lowers the percentage of exploitable bugs isn't all bad (even if it's not perfect)? Until about 2 years ago there wasn't really a better solution around...
- InclinedPlane 13y agoIt's usually pretty difficult to create an exploitable remote code execution bug in code without very obviously heinous (read: amateur level) code. Whereas with buffer overflows it's fairly trivial. Moreover, in order to exploit a more complex vulnerability an attacker would need to know something about the nature of the vulnerability, making it more difficult to come up with exactly the right steps to exploit it to maximum advantage. In contrast, with a typical buffer overflow you only need to tune how much junk to throw at the buffer before you start getting code to execute, and then you can just inject whatever existing payload you have already. This is so trivial that there are now tools designed to automate the process.
- 13y ago
- Skinney 13y ago> These APIs do not "fail safe" > This is still a bug in your program Define "fail safe". They don't write more than what you want it to write, which could be exactly what you're looking for. Does it hide bugs? Maybe (depends on how you use it), but it's easier to figure out a bug regarding a short string, than trying to figure out why the program is crashing because you have a buffer overflow somewhere in your program. > Instead the program must reallocate or signal an error Who is he, to tell us what is and what isn't acceptable behaviour in our programs? There are numerous cases where discarding some parts of a string is perfectly acceptable. > I can construct you cases where the use of these stupid functions is creating new security problem. Sure, I can construct such cases for almost every single function in the C std library. This is C, that's the tradeof we make for speed and efficiency.
- brokenparser 13y agostrlcpy is better because strncpy doesn't indicate an error (and strcpy, well, we know what that does), not because it prevents a crash. I'd rather debug a crash because in that case, I get a nice stack trace and core dump to gaze at. Also, I disagree C is a tradeoff for speed an efficiency. Runtime speed and efficiency? Development time perhaps? I can easily write parallel code in scala that defeats idiomatic C on both. I use C when I want something portable. I can't use the scala code on a tiny mips device, but I can port the C code if it's reasonably well written. Same goes for any other target, there's nothing so pervasive as C.
- Skinney 13y ago> strlcpy is better because strncpy... I was mostly thinking about about strlcpy and strcpy. The latter indicates no error and can cause a buffer overflow, which IMHO is harder to debug than erronous logic due to a stringer that is shorter than it is supposed to be. > I get a nice stack trace and core dump to gaze at I keep forgetting that C actually has stack traces on normal platforms. We lose all information when the program crashes, stupid custom proprietary platform. > Also, I disagree C is a tradeoff for speed an efficiency In C, you sacrifice safety (undefined behaviour etc.) for speed. Rust gives you safety, but (acording to themselves) you can't expect Rust to beat C/C++ in performance for certain operations. > that defeats idiomatic C on both But has a minimum of three times the memory overhead. I could write a paralell Clojure application that is way easier to reason about in less time than I could write said application in Scala, but I sacrifice memory and speed efficiency. Every language has tradeofs. C sacrifices safety for speed and memory efficiency. > I can't use the scala code on a tiny mips device Why not? There is a JVM for mips isn't there? Or don't you have enough memory? JVM languages uses alot more memory than a native program, that doesn't necessarily make JVM-based languages any less portable. The same .jar file can be executed on Windows, Linux, Mac and more operating systems without a recompile. That's portability that's hard to beat. Considering you can also compile Java applications for iOS now, and of course Android as well, JVM-based languages are remarkably portable, and I'd argue that they are more portable than C. The reasons for using C IMHO, is that it's fast, memory efficient, doesn't do any magic (like GC and JIT compiling and whatnot), and that it doesn't require warmup to be fast. It is however slower to develop in, and the language is inherently unsafe. Last but not least, it's important to use the right tool for the right job. I would never write a VM in a JVM-based language (I'm writing one in C now, fun fun fun!). On the flip-side I would never write a GUI application in C.
- anon1385 13y agoYou think that *((char *) mempcpy (dst, src, n)) = '\0'; is a good solution then? Also, I don't know where this 'srlcpy silently fails' meme comes from. It's your responsibility to check the return value. It only 'silently fails' if you don't check. You can argue about whether it is a good idea or not to make client code responsible for that kind of thing but it is the C philosophy. If you think string copy functions should call abort on failure then, like Theo says in the OP, implement that and submit patches to all those projects using strcpy.
- adobriyan 13y ago> mempcpy For chain copying? Why not.
- saurik 13y agoThe reality is that both strcpy and strlcpy require the person using the API to use it correctly in order to not end up with a buggy program; but, in the real world, people hype strlcpy as being fundamentally safer than strcpy, and so if you actually look into how people use strlcpy no one ever checks its return value (even projects like OpenSSH). For more on this (and a specific ludicrous example from OpenSSH where someone spent the time to write a comment above the strlcpy about truncation and yet still didn't actually bother to check the return value of the function), please see this response I made to someone else: https://news.ycombinator.com/item?id=6940724 https://news.ycombinator.com/item?id=6940724
- _pmf_ 13y ago> Ah, there's some classic Drepper. I think it's actually quite appropriate that the maintainer of the software that (in one form or the other) is present on hundreds of millions of devices is a bit conservative.
- fidotron 13y agoA massive proportion of Linux devices are actually using Bionic or uClibc instead of the gnu libc. While superficially the reasoning may seem to be purely technical, freedom from Drepper is not an unacknowledged feature of these alternatives.
- tobiasu 13y agoglibc has a fork/flavour called eglibc because Ulrich consistently refused to integrate work that wasn't relevant to his big iron Red Hat server world. For example he's "famous" for refusing to patch ldd to work with any posix shell instead of bash: https://www.sourceware.org/bugzilla/show_bug.cgi?id=3266 https://www.sourceware.org/bugzilla/show_bug.cgi?id=3266 It was recently fixed, but only after more considerate guys took over maintainership...
- TheCondor 13y agoWouldn't a "safe" api require size parameters on both the source and destination? We have historically had security bugs that over run on both sides, right? Why only address half the problem? I like C, I use it, I use it in my work, but I simply cannot understand why we don't pay more attention to its huge problems to give compatibility with an ancient standard library.
- clarry 13y agoThat really depends. Error checking gets suddenly even more complicated if you also need some way to handle the case where the source string isn't a string. If you don't know what your source is, how can you pass its length to the function? On the other hand, if you know its length, then you should know that it is NUL-terminated (i.e. it is a string), or you should be able to NUL-terminate it. Perhaps there is some scenario where a helper function could also be used to force NUL-termination on a fixed-size source buffer (say, after fetching input with read()). But I cannot think of one that makes a lot of sense. For instance, having to forcibly NUL-terminate a bufferful of data from read() would imply data loss: you've read too much and now one byte was replaced with a zero. How would your safe function signal this error condition, and how would you react? In the end you'd have to fix the broken code so it leaves one space for the NUL. By contrast, strcat/cpy generally speaking do not catch broken-code errors (that is difficult anyway); they only implement some correct code. Also, reading past the end of the input buffer is much less likely to lead to an exploitable hole (unless you count DoS, but then how would your "safe" function react anyway?). EDIT: One of the goals in OBSD helper functions is simplicity. The rationale being that if doing the right thing is too complicated, the right thing won't be done (lazy programmers!). And, of course, all complications make human error more likely. This is why strlcat and strlcpy are not the solution to any and all string handling ever; sometimes you need something smarter, e.g. for performance. But these functions are great when they're good enough (which is plenty often, if you trust any of the code that actually uses these!).
- tedunangst 13y agoIf you never write a string past the end of a buffer, you will never read past the end. If your program starts in a correct state, write operations are where it can possibly transition to an incorrect state. The strlcpy function helps maintain the invariant that all strings in your program are actually strings, not unbounded nonsense, and so the source will be safe to use.