News for hackers. Stuff that matters.
Re:Tell HN: C Experts Panel – Ask us anything about C by hyc_symas on Tuesday April 14, 2020 @02:41PM (#22869781)
The standard string library is still pretty bad. This would have been a much better addition for safe strcpy.

Safe strcpy

    char *stecpy(char *d, const char *s, const char *e)
    {
     while (d < e && *s)
      *d++ = *s++;
     if (d < e)
      *d = '\0';
     return d;
    }

    main() {
      char buf[64];
      char *ptr, *end = buf+sizeof(buf) ;

      ptr = stecpy(buf, "hello", end);
      ptr = stecpy(ptr, " world", end);
    }

Existing solutions are still error-prone, requiring continual recalculation of buffer len after each use in a long sequence, when the only thing that matters is where the buffer ends, which is effectively a constant across multiple calls.

What are the chances of getting something like this added to the standard library?

Re:Tell HN: C Experts Panel – Ask us anything about C by pascal_cuoq on Tuesday April 14, 2020 @03:42PM (#22870537)
For what it's worth, I personally like this approach, because there are some cases in which it requires less arithmetic in order to be used correctly. And it lends itself better to some forms of static analysis, for similar reasons, in the following sense:

There is the problem of detecting that the function overflows despite being a “safe” function. And there is the problem of precisely predicting what happens after the call, because there might be an undefined behavior in that part of the execution. When writing to, say, a member of a struct, you pass the address of the next member and the analyzer can safely assume that that member and the following ones are not modified. With a function that receives a length, the analyzer has to detect that if the pointer passed points 5 bytes before the end of the destination, the accompanying size it 5, if the pointer points 4 bytes before the end the accompanying size is 4, etc.

This is a much more difficult problem, and as soon as the analyzer fails to capture this information, it appears that the safe function a) might not be called safely and b) might overwrite the following members of the struct.

a) is a false positive, and b) generally implies tons of false positives in the remainder of the analysis.

(In this discussion I assume that you want to allow a call to a memory function to access several members of a struct. You can also choose to forbid this, but then you run into a different problem, which is that C programs do this on purpose more often than you'd think.)

Re:Tell HN: C Experts Panel – Ask us anything about C by msebor on Tuesday April 14, 2020 @03:17PM (#22870246)
There are many improved versions of string APIs out there, too many in fact to choose from, and most suffer from one flaw or another, depending on one's point of view. Most of my recent proposals to incorporate some that do solve some of the most glaring problems and that have been widely available for a decade or more and are even parts of other standards (POSIX) have been rejected by the committee. I think only memccpy and strdup and strdndup were added for C2X. (See http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2349.htm for an overview.)
Re:Tell HN: C Experts Panel – Ask us anything about C by AceJohnny2 on Tuesday April 14, 2020 @03:36PM (#22870481)
> Most of my recent proposals [...] have been rejected by the committee.

Does anyone have insight on why?

Re:Tell HN: C Experts Panel – Ask us anything about C by saagarjha on Tuesday April 14, 2020 @08:20PM (#22873051)
memccpy is a very welcome addition in the front of copying strings; what else were you thinking of proposing?
Re:Tell HN: C Experts Panel – Ask us anything about C by saagarjha on Tuesday April 14, 2020 @08:17PM (#22873032)
I recently looked at a number of string copying functions, as well as came up with an API a bit similar to yours: https://saagarjha.com/blog/2020/04/12/designing-a-better-str... (mine indicates overflow more clearly). memccpy, which is coming in C2X, makes designing these kinds of things finally possible.
Re:Tell HN: C Experts Panel – Ask us anything about C by doublesCs on Tuesday April 14, 2020 @03:33PM (#22870444)
What's wrong with:

    *p += sprintf(*p, "hello");
    *p += sprintf(*p, "world");
Re:Tell HN: C Experts Panel – Ask us anything about C by spc476 on Tuesday April 14, 2020 @06:33PM (#22872309)
Well, that should be `snprintf()` to start with, but even with that, there are issues. The return type of `snprintf()` is `int`, so it can return a negative value if there was some error, so you have to check for that case. That out of the way, a positive return value is (and I'm quoting from the man page on my system) "[i]f the output was truncated due to this limit then the return value is the number of characters which would have been written to the final string if enough space had been available." So to safely use `snprintf()` the code would look something like:

    int size = snprintf(NULL,0,"some format string blah blah ...");
    if (size < 0) error();
    if (size == INT_MAX)
      error(); // because we need one more byte to store the NUL byte
    size++;
    char *p = malloc(size);
    if (p == NULL)
      error();
    int newsize = snprintf(p,size,"some format string blah blabh ... ");
    if (newsize < 0) error();
    if (newsize > size)
    {
      // ... um ... we still got truncated?
    }
Yes, using NULL with `snprintf()` if the size is 0 is allowed by C99 (I just checked the spec).

One thing I've noticed about the C standard library is that is seems adverse to functions allocating memory (outside of `malloc()`, `calloc()` and `realloc()`). I wonder if this has something to do with embedded systems?

Re:Tell HN: C Experts Panel – Ask us anything about C by SAI_Peregrinus on Tuesday April 14, 2020 @08:35PM (#22873136)
Not just embedded systems, also OSes. C's standard library should generally work without the existence of a heap. After all, you have to create the heap using C before you can allocate from it.
Re:Tell HN: C Experts Panel – Ask us anything about C by saagarjha on Tuesday April 14, 2020 @09:51PM (#22873647)
malloc is a required part of ISO C, though.
Re:Tell HN: C Experts Panel – Ask us anything about C by flatfinger on Friday April 17, 2020 @04:54PM (#22902891)
Functions like malloc are only required for hosted implementations. Many operating systems are built using freestanding implementations.

Further, on many platforms, one should avoid using malloc() unless portability is more important than performance or safety. Some operating systems support useful features like the ability to allocate objects with different expected lifetimes in different heaps, so as to help avoid fragmentation, or arrange to have allocations a program can survive without fail while there is still enough memory to handle critical allocations. Any library that insists upon using "malloc()" will be less than ideal for use with any such operating system.

Re:Tell HN: C Experts Panel – Ask us anything about C by saagarjha on Tuesday April 14, 2020 @08:23PM (#22873061)
Also, the return type being int means that there's a limit to the length of your string…
Re:Tell HN: C Experts Panel – Ask us anything about C by wahern on Tuesday April 14, 2020 @04:50PM (#22871276)
Perhaps you meant snprintf. But snprintf can fail on allocation failure, fail if the buffer size is > INT_MAX, and in general isn't very light weight--last time I checked glibc, snprintf was a thin wrapper around the printf machinery and is not for the faint of heart--e.g. initializing a proxy FILE object, lots of malloc interspersed with attempts to avoid malloc by using alloca.

It can also fail on bad format specifiers--not directly irrelevant here except that it forces snprintf to have a signed return value, and mixing signed (the return value) and unsigned (the size limit parameter) types is usually bad hygiene, especially in interfaces intended to obviate buffer overflows.

Re:Tell HN: C Experts Panel – Ask us anything about C by pjscott on Tuesday April 14, 2020 @04:10PM (#22870858)
That could lead to buffer overflow.����
Re:Tell HN: C Experts Panel – Ask us anything about C by doublesCs on Tuesday April 14, 2020 @05:54PM (#22871980)
When I wrote that, I had in mind the observation about continued recalculation of buffer len. My suggestion has no such thing. It looks so good that I imagine this was probably how it was intended to be used. With that in mind, isn't it the user's job to know the size of the buffers he's using? Doesn't expecting that the function know about buffer size go against the single responsibility principle?

I'm new to C, in case you couldn't tell.

Re:Tell HN: C Experts Panel – Ask us anything about C by pascal_cuoq on Tuesday April 14, 2020 @06:26PM (#22872245)
The problem in practice is that you do not write “hello” and “world” to the destination buffer. You write data that is computed more or less directly from user inputs. Often a malicious user.

So the user only needs to find a way to make the data longer than the developer expected. This may be very simple: the developer may have written a screensaver to accept 20 characters for a password, because who has a longer password than this? Everyone knows that only the first 8 characters matter anyway. (This may have been literally true a long time ago, I think, although it's terrible design. Anyway only 8 characters of hash were stored, so in a sense characters after the first 8 did not buy you as much security as the first 8, even if it was not literally true.)

And this is how there were screensavers that, when you input ~500 characters into the password field, would simply crash and leave the applications they were hiding visible and ready for user input. This is an actual security bug that has happened in actual Unix screensavers. The screensavers were written in C.

And long story short, we have been having the exact same problem approximately once a week for the last 25 years. Many people agree that it is urgent to finally fix this, especially as the consequences are getting worse and worse as computers are more connected.

One solution that some favor is functions that make it easier not to overflow buffers because you tell them the size of the buffer instead of trying to guess in advance how much is enough for all possible data that may be written in the buffer. This is the thing being discussed in this thread. The function sprintf is not a contender in this discussion. The function snprintf could be, if used wisely, but it is a bit unwieldy and the OP's proposal has a specific advantage: you compute the end pointer only once, because this is the invariant.

Re:Tell HN: C Experts Panel – Ask us anything about C by hyc_symas on Wednesday April 15, 2020 @04:32PM (#22882678)
An analogous seprintf() would probably be a good thing to add too, where the buffer end is passed in instead of a buffer length. I would still have it return a pointer to the end of what was copied. Anyone can calculate the length if they need to, by subtracting the original pointer from the returned pointer.

    char *seprintf(char *str, char *end, const char *format, ...);
Re:Tell HN: C Experts Panel – Ask us anything about C by souprock on Wednesday April 15, 2020 @12:23AM (#22874471)
I think sprintf and gets can be perfectly secure interfaces. The standard just needs to specify them in a way that causes overflows to raise signals. This is probably more for POSIX and UNIX, since I think it requires the concept of memory mappings. For example:

Start by specifying that memcpy goes by increasing address. This can be done by specifying that no pages to be written by memcpy can be written to until after all pages with lower addresses have been accessed by memcpy. (it is OK to read forwards and then write backwards; the first access must not skip pages)

Next, specify sprintf and gets in terms of memcpy. The output is written as if by memcpy.

The user may then place a PROT_NONE page of memory after the buffer. Since the pages are being accessed by address order, the PROT_NONE page will safely stop the buffer overflow. The user can have a signal handler deal with the problem. It can exit or map in more memory. If we require sprintf and gets to be async-signal-safe, then the signal handler can also siglongjmp out of the problem.

Re:Tell HN: C Experts Panel – Ask us anything about C by saagarjha on Wednesday April 15, 2020 @04:56AM (#22875778)
Surely you don’t expect every stack buffer to have a hard page placed after it to protect from overflows?
Re:Tell HN: C Experts Panel – Ask us anything about C by clarry on Tuesday April 14, 2020 @06:24PM (#22872229)
> With that in mind, isn't it the user's job to know the size of the buffers he's using?

Yes. The user knows the size of his buffer, and then passes that knowledge on to the string constructing functions so that they do not overflow the buffer.

> Doesn't expecting that the function know about buffer size go against the single responsibility principle?

What's single responsibility again? "Execute this one assembly instruction"?

What you want from standard library functions is, usually, "construct a string into this buffer (whose size is N)."

Re:Tell HN: C Experts Panel – Ask us anything about C by ftvy on Tuesday April 14, 2020 @04:56PM (#22871334)
It looks like you'd be dereferencing the pointer p, but you'd also need to make sure that what p points to has enough memory.