To provide a consistent approach to error handling and truncation management, we provide `os::xxx` wrapper functions as described below and forbid the use of the library `::vsnprintf` and `::snprintf`.
The potential errors are, generally speaking, not something we should encounter in our own well-written code:
- encoding error: not applicable as we are not using extended character sets
- invalid parameters (null buffers, specifying a limit > size of the buffer [Windows], things of this nature)
- mal-formed formatting directives
- overflow error (POSIX) if the required buffer size exceeds INT_MAX (as we return `int`).
As these should simply never occur, we handle the checks for -1 at the lowest-level (`os::vsnprintf`) with an assertion, and accompanying precondition assertions.
The potential clients of this API then fall into a number of camps:
1. Those who have sized their buffer correctly, don't need the return value for subsequent use, and for whom truncation (if it were possible) would be a programming error.
For these clients we have `void os::snprintf_checked` - which returns nothing and asserts on truncation.
2. Those who have sized their buffer correctly, but do need the return value for subsequent operations (e.g. chains of `snprintf` where you advance the buffer pointer based on previous writes), but again for whom truncation should never happen.
For these clients we have `os::snprintf`, but they have to add their own assertion for no truncation.
3. Those who present a buffer but know that truncation is a possibility, but don't need to do anything about it themselves, and for whom the return value is of no use.
These clients also use `os::snprintf_checked`. The truncation assertion can be useful for guiding buffer sizing decisions, but in product mode truncation is not an error.
4. Those who present a buffer but know that truncation is a possibility, and either need to handle it themselves, or else need to use the return value in subsequent operations.
These clients are also directed to use `os::snprintf`.
In summary we provide the following API:
- `[[nodiscard]] int os::vsnprintf` is the building block for the other methods, it:
- asserts on precondition failures
- asserts on error
- guarantees null-termination in the case of unexpected errors (as the standards are still unclear on that point
- is declared `[[nodiscard[]]` so that callers cannot ignore the return value (they can explicitly cast to `void` to indicate they dn't need it)
- `void os::snprintf_checked`
- calls `os::vnsprintf`` so asserts on errors
- asserts on truncation
- [[nodiscard]] int os::snprintf
- calls `os::vnsprintf`` so asserts on errors
In terms of the effects on the existing code we:
- Change callers of `::snprintf`/`os::snprintf` that ignore the return value and ensure the buffer is large enough to use `os::snprintf_checked`
- those that allow truncation to happen must use `os::snprintf`.
- Change all callers of `::snprintf`/`os::snprintf` that use the return value to use `os::snprintf`, plus any additional assertions needed
- Change the 9 callers of `os::snprintf_checked` that do use the return value, to use `os::snprintf` with their own assertions added
- Callers of `os::vnsprintf` are adjusted as needed
- blocks
-
JDK-8198918 jio_snprintf and friends are not checked by -Wformat
-
- Open
-
- relates to
-
JDK-8365896 Remove unnecessary explicit buffer nul-termination after using os::snprintf
-
- Open
-
-
JDK-8368816 Convert jio_snprintf chains to stringStream
-
- Open
-
-
JDK-8347719 [REDO] Portable implementation of FORBID_C_FUNCTION and ALLOW_C_FUNCTION
-
- Resolved
-
-
JDK-8313396 Portable implementation of FORBID_C_FUNCTION and ALLOW_C_FUNCTION
-
- Closed
-
- links to
-
Commit(master) openjdk/jdk/80ab094a
-
Review(master) openjdk/jdk/26470
-
Review(master) openjdk/jdk/26849