Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C/C++: maximum size of errno-associated strings (at compile-time)

Tags:

c++

c

Question

Is there any way to get the maximum size of any string correlated with errno at compile time (at preprocessor time would be even better)? E.g. an upper bound on strlen(strerror(errno))?

My Thoughts

The best I can think of is running a program to do a brute-force search over the range of an int, over each locale, to get the string associated with each {errno, locale} pair, get its size, and generate a header on that system, then hooking that into e.g. a makefile or autoconf or whatever. I can't think of a better way to do it, but it seems ridiculous that it would be so: the standard library for a system has that information built-in, if only implicitly. Is there really no good way to get that information?

Okay, I'll admit the C and/or C++ standards might permit for error strings generated at runtime, with e.g. specific-to-circumstance messages (e.g. strerror(EINVAL) giving a string derived from other runtime metadata set when errno was last set, or something) - not sure if that is allowed, and I'd actually welcome such an implementation, but I've never heard of one existing which did so, or had more than one string for a given {errno, locale} pair.

Motivation

For context, what I specifically wanted (but I think this question is valuable in a more general way, as was discussed amongst the comments) that led to this question was to be able to use the error string associated with errno in the syscall/function writev. In my specific usecase, I was using strings out of argv and errno-linked strings. This set my "worst-case" length to ARG_MAX + some max errno string length + size of a few other small strings).

Every *nix document I've consulted seems to indicate writev will (or "may", for what little good that difference makes in this case) error out with errno set to EINVAL if the sum of the iov_len values overflows SSIZE_MAX. Intuitively, I know every errno string I've seen is very short, and in practice this is a non-issue. But I don't want my code mysteriously failing to print an error at all on some system if it's possible for this assumption to be false. So I wrote code to handle such a case - but at the same time, I don't want that additional code being compiled in for the platforms which generally clearly don't need it.

The combined input of the answers and comments so far is making me lean towards thinking that in my particular use-case, the "right" solution is to just truncate obscenely long messages - but this is why I asked the question how I did initially: such information would also help select a size for a buffer to strerror_r/strerror_s (*nix/Windows respectively), and even a negative answer (e.g. "you can't really do it") is in my view useful for others' education.

Related

This question contains answers for the strings given by strerror_r on VxWorks, but I don't feel comfortable generalizing that to all systems.

like image 609
mtraceur Avatar asked Mar 30 '16 20:03

mtraceur


4 Answers

The C library that you build against may not be the same (ABI compatible C library maybe used) or even exact version of the C library (On GNU/Linux consider glibc 2.2.5 vs. glibc 2.23) that you run against, therefore computing the maximum size of the locale-dependent string returned from strerror can only be done at runtime during process execution. On top of this the locale translations may be updated on the target system at any time, and this again invalidates any pre-computation of this upper bound.

Unfortunately there is no guarantee that the values returned by strerror are constant for the lifetime of the process, and so they may also change at a later time, thus invalidating any early computation of the bound.

I suggest using strerror_r to save the error string and avoid any issues with non-multi-thread aware libraries that might call sterror and possibly change the result of the string as you are copying it. Then instead of translating the string on-the-fly you would use the saved result, and potentially truncate to SSIZE_MAX (never going to happen in reality).

like image 94
Carlos O'Donell Avatar answered Nov 20 '22 19:11

Carlos O'Donell


I'm not aware that the C or C++ standards make any assertions regarding the length of these messages. The platforms you're interested in might provide some stronger implementation-defined guarantees, though.

For example, for POSIX systems, I found the following in limits.h.

The following constants shall be defined on all implementations in <limits.h>:

  • […]
  • {NL_TEXTMAX}
    Maximum number of bytes in a message string.
    Minimum Acceptable Value: {_POSIX2_LINE_MAX}

I believe that error messages produced by strerror would fall into this category.

That said, I'm unable to get this macro on my system. However, I do have _POSIX2_LINE_MAX (from <unistd.h>). It is #defined to 2048. Since the standard only says that this is a lower bound, that might not be too helpful, though.

like image 23
5gon12eder Avatar answered Nov 20 '22 21:11

5gon12eder


The standards make no guarantees about the size limits of the null-terminated string returned by strerror.

In practice, this is never going to be an issue. However, if you're that paranoid about it, I would suggest that you just copy the string returned from strerr and clamp its length to SSIZE_MAX before passing it to writev.

like image 3
Colin Basnett Avatar answered Nov 20 '22 20:11

Colin Basnett


It is safe to assume that SSIZE_MAX will be greater than the longest string (character array) that strerror returns in a normal C or C++ system. This is because usable system memory (usable directly by your C program) can be no larger than SIZE_MAX (an unsigned integer value) and SSIZE_MAX will have at least the same number of bits so using 2's compliment math to account for the signed nature of SSIZE_MAX (and ssize_t) SSIZE_MAX will be at least 1/2 the size of system memory.

like image 2
nategoose Avatar answered Nov 20 '22 19:11

nategoose