This is an archive of the discontinued LLVM Phabricator instance.

[libc] add integer writing to printf
ClosedPublic

Authored by michaelrj on Jun 10 2022, 11:24 AM.

Details

Summary

This patch adds %n to printf, as well as a compiler flag to disable it.
This is due to it having serious security issues when misused.

Diff Detail

Event Timeline

michaelrj created this revision.Jun 10 2022, 11:24 AM
Herald added projects: Restricted Project, Restricted Project. · View Herald TranscriptJun 10 2022, 11:24 AM
michaelrj requested review of this revision.Jun 10 2022, 11:24 AM
tschuett added a comment.EditedJun 10 2022, 12:26 PM

I know that you are tied to the standards. Maybe expose llvmllibc extensions of safe or hardened functions, e.g., save_printf.

While the idea of having a seperate "safe" version of printf is not inherently bad, it's already been tried to little success. In the standard right now is the definitions for the _s versions of printf functions. These have bounds checking and disable %n, but are also not widely used. Convincing developers to switch is hard, so my current plan is to use the compile-time switch I've added to disable %n by default in the existing printf functions. This provides the security enhancements by default for all of the people who don't care/didn't know that %n existed, while still allowing people to turn it back on if they are really sure they know what they're doing.

dxf added a subscriber: dxf.Jun 10 2022, 1:30 PM

I know that you are tied to the standards. Maybe expose llvmllibc extensions of safe or hardened functions, e.g., save_printf.

Some libc implementations support compiling sources with FORTIFY_SOURCE, which (with compiler support) can call _chk() versions of functions like printf().

How best to support FORTIFY_SOURCE is an open question but it is something that is being thought about. Is this an area you are interested in helping with?

Clang can also do things with _FORTIFY_SOURCE:
https://reviews.llvm.org/D58797

dxf added a comment.Jun 10 2022, 2:04 PM

Clang can also do things with _FORTIFY_SOURCE:
https://reviews.llvm.org/D58797

And that's one potential approach, to do more things with FORTIFY_SOURCE in Clang (and not require libc to carry special FORTIFY bits).

Do you really need compiler support?

#ifdef FORTIFY_SOURCE
#include "checked_printf.h"
#else
#include "fast_printf.h"
#endif
dxf added a comment.Jun 10 2022, 2:43 PM

FORTIFY_SOURCE support has typically been provided through a combination of compiler and libc support (e.g. gcc and glibc, Clang and bionic). Multiple applications can link against the same libc, and some will want the FORTIFY_SOURCE versions and some will not, but all user code still just wants to call "mempcpy()" or whatever. When FORTIFY_SOURCE is enabled, the compiler can know to pick the _chk versions of the functions (and do some other work as well).

Maybe I'm stupid, but I would expect both headers to expose a printf.

int printf(const char * restrict format, ...) {
  do some checks here
}
dxf added a comment.Jun 10 2022, 5:57 PM

You could certainly do something like that.

FORTIFY_SOURCE is a long-established way of inserting a relatively low-cost runtime check to a number of libc functions. The compiler can also do some static checks and it can also decide things like "should this call to mempcpy() invoke the regular mempcpy(), or the one with extra checks?" and not simply call the one with extra checks every time.

For many projects, a "hardened" libc is nice but not sufficient, and they use tools like ASan instead (which can help catch memory problems across all the code and not just in the libc functions).

I think if we want to have "safe" versions of these functions we should do it via the FORTIFY_SOURCE mechanism since both glibc and bionic support it. LLVM's libc could carry code (like glibc and bionic) to support FORTIFY, if someone wants to contribute those patches. There might be other ways to achieve the same functionality. I'd like to continue this discussion, but that's probably best done on discourse.

rebase onto new changes, specifically converters getting return values.

lntue added inline comments.Jun 28 2022, 11:24 AM
libc/src/stdio/printf_core/write_int_converter.h
25

How is this return value defined? Aren't errors normally -1? And do we have tests for this?

michaelrj marked an inline comment as done.

add test for the nullptr check

libc/src/stdio/printf_core/write_int_converter.h
25

this check is not in any of the specifications, and is a convenience feature that I added. The return value is negative, since the return value for printf is negative on errors. As for why it's -3, that's because -1 and -2 are used for file writing errors, and this allows me as the developer to tell what the error was.

I have now added a test to check that it returns an error value when passed a null pointer.

lntue accepted this revision.Jun 28 2022, 12:27 PM
lntue added inline comments.
libc/src/stdio/printf_core/write_int_converter.h
25

Sound like we'll need some commonly defined macros for these values as the number of different errors grows.

This revision is now accepted and ready to land.Jun 28 2022, 12:27 PM
michaelrj updated this revision to Diff 440754.Jun 28 2022, 1:39 PM
michaelrj marked an inline comment as done.

add comment explaining the negative return value.

libc/src/stdio/printf_core/write_int_converter.h
25

I don't expect the number of errors to grow much, but I will make a followup patch adding the error values to core_structs.h.

This revision was automatically updated to reflect the committed changes.