2

I have a C++ program for which validated API users (not interactive users, so there is no major security/integrity exposure here) can pass a parameter that becomes a format string for printf() (actually, for vsnprintf()). Of course, bad format specifiers make the program blow up. I have documented that and it is acceptable -- but is there any way to trap printf() errors rather than having the C runtime assert?

Environment is pretty much standard Posix (z/OS XLC, FWIW).

Mark Rotteveel
  • 100,966
  • 191
  • 140
  • 197
Charles
  • 479
  • 1
  • 3
  • 13
  • Parse/count the `%something` yourself? – deviantfan Apr 21 '15 at 10:36
  • 2
    Nothing standard, but some compilers offer compile-time type checking for printf style functions. For example, GCC has a [`format` attribute](https://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html). – Mike Seymour Apr 21 '15 at 10:36
  • I've used some function (don't recall the name exactly) in Windows -- _crt_something() -- that let me do this sort of thing for strftime() -- but I don't have that here. – Charles Apr 21 '15 at 10:40
  • Who do you get `printf` to crash your program? – WaeCo Apr 21 '15 at 10:41
  • Yeah, I could do a full parse myself. Heck, I could write my own vsnprintf()! Trying to avoid that. Counting arguments is not enough. There is no end to the variety of bad arguments you can pass printf()! – Charles Apr 21 '15 at 10:42
  • I should add that compile-time checking will not solve the problem, because the format string is known only at runtime as it comes as a parameter over an incoming API (my program's API). – Charles Apr 21 '15 at 10:45
  • To clarify: the users are not C++ programmers invoking your API functions, but only interact with your program via stdin/sockets/files/whatever (right?). They can only pass strings/bytes over that, and not e.g. objects (right?). – chi Apr 21 '15 at 11:03
  • @WaeCo: Not sure what you are asking. – Charles Apr 22 '15 at 00:30
  • @chi: The users are C/C++ or (gasp) assembler language programmers passing a parameter to a callable API. It is not the main function of the API, but one of my users is calling from a situation in which he is running as part of the kernel (more or less) and cannot use I/O functions, so I have implemented a way for him or others to output error and status messages on my log. They can pass up to four variables and a "printf() style" text message. They are skilled programmers but stuff happens. The doc emphasizes that stuff should happen in test, not production. – Charles Apr 22 '15 at 00:33
  • Ran into a maximum comment length. ... And again, the API is only available from a program with specific privileges, so this interface is not exposed to the world. No sockets! And yes, strings of bytes/characters, not objects. – Charles Apr 22 '15 at 00:36
  • On these questions you always end up explaining and explaining. The obvious question would be "if the caller can't do printf's or similar how come I can?" and the answer is they are separate processes joined by an in-memory round-robin queue. And yes, that part all works. – Charles Apr 22 '15 at 00:40

4 Answers4

1

Use sigsetjmp()/siglongjmp().

Call sigsetjmp() prior to using the potentially bad format, and install a custom handler for SIGSEGV and SIGBUS that calls siglongjmp() with the context from the sigsetjmp() call.

Just be sure to use restore the signal handlers after you don't need them lest a SIGSEGV somewhere else cause some unexpected results.

Example here:

http://www-01.ibm.com/support/knowledgecenter/ssw_ibm_i_72/apis/sigsetj.htm

Andrew Henle
  • 32,625
  • 3
  • 24
  • 56
  • In addition: [http://stackoverflow.com/a/918891/3568427](http://stackoverflow.com/a/918891/3568427) – WaeCo Apr 21 '15 at 11:02
  • To be pedantic, that example is incorrect in calling `printf()` in a signal handler since `printff()` isn't async-signal-safe. Never mind tossing an exception from within a signal handler, That's **very** platform-specific. see here: https://gcc.gnu.org/ml/gcc-help/2011-08/msg00253.html – Andrew Henle Apr 21 '15 at 11:21
  • Thanks. This looks like it has potential. I will have to give it a try to be sure. – Charles Apr 21 '15 at 12:44
  • This approach seems unreliable and dangerous to me -- undefined behavior is not guaranteed to raise a signal and (worse) might have other less-obvious side effects. In particular I would be worried about silent data corruption that might occur instead of (or in addition to) a SIGSEGV or SIGBUS signal. – Jeremy Friesner Jun 11 '15 at 02:02
0

Please see to BOOST format library - http://www.boost.org/doc/libs/1_58_0/libs/format/

The format library provides a class for formatting arguments according to a format-string, as does printf, but with two major differences :

format sends the arguments to an internal stream, and so is entirely type-safe and naturally supports all user-defined types.

You can use C++ exception with this library.

Community
  • 1
  • 1
SergV
  • 1,269
  • 8
  • 20
0

I was tempted to recommend a simple C++ style try/catch:

. . .
try
  printf(...)
catch (...)
  printf("An error happened handling your printf string!")
...

But I figure you tried that and have a reason why it doesn't meet your needs.

The deeper alternative is an LE Condition Handler - it's essentially a callback routine that gives you control any time an exception occurs, and it includes a way to resume execution after an error. Read more by Googling CEEHDLR or see the "Handling error conditions, exceptions, and signals" section of the XL C/C++ Programming Guide.

In a pinch, you can also wrap your calls to printf() with ESTAE/ESPIE macros to trap the error before the runtime sees it...yes, it takes a few lines of assembler, but many times it's possible to get way more granular this way.

Best of luck!

Valerie R
  • 1,769
  • 9
  • 29
  • 1
    printf() is a C function and therefore cannot and does not throw C++ exceptions; that is why a C++ style try/catch won't help. – Jeremy Friesner Jun 11 '15 at 02:04
-1

Answering title:

if(printf("...") < 0)
    {
        perror("printf error message");
        exit;
    }

Addition. Working example

wchar_t wide[2];
wide[0] = 129;
wide[1] = 0;
    
if(printf("%ls", wide) < 0)
    perror("detect error");
zkutch
  • 359
  • 3
  • 7
  • Won't usually work for `if(printf("%s", 1234) < 0)` which is likely to *crash* – Basile Starynkevitch Apr 05 '21 at 08:48
  • @Basile Starynkevitch. What you bring is rather answer to how formatting error leads to SIGSEGV, Segmentation fault, then to possibility to trap errors of printf. From standard, for example C2011, 7.21.6.3 we read, that negativity comes from encoding error, see addition above, or output error. My answer(+ Addition) shows , that it usually works, if/when you are using correct checking. – zkutch Apr 06 '21 at 09:44
  • For down voters: constructive critique is welcome, especially when answer is correct. – zkutch Apr 06 '21 at 09:47