This is a proposal to standardize on the use of `os::snprintf` and 
`os::snprintf`_checked across the hotspot code base, and to disallow use of the 
C library variants. (It does not touch use of `jio_printf` at all.)

From: https://bugs.openjdk.org/browse/JDK-8347707

The platform `snprintf/vsnprintf` returns -1 on error, else if the buffer is 
large enough returns the number of bytes written (excluding the null byte), 
else (buffer is too small) the number of characters (excluding the terminating 
null byte) which would have been written to the final string if enough space 
had been available. Thus, a return value of size or more means that the output 
was truncated.

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 don'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

The PR  is comprising multiple dependent commits so that you can view things in 
stages.  There are 46 modified files. The bulk of the changes replace calls to 
`snprintf`/`os::snprintf` with calls to `os::snprintf_checked`.

Note the use of `[[nodiscard]]` is permitted but not yet documented as such in 
the style-guide.

-------------

Commit messages:
 - Merge
 - Make os::snprintf "nodiscard" and adjust final callsites
 - Convert os::snprintf() to os::snprintf_checked() where appropriate.
 - Forbid C library snprintf
 - Change return-value using snprintf to explicit os::snprintf
 - Change raw snprintf to os::snprintf_checked, whereever truncation would not
 - Change os::snprintf_checked to be void function.
 - Mark os::vsnprintf as nodiscard and update the callsites.

Changes: https://git.openjdk.org/jdk/pull/26849/files
  Webrev: https://webrevs.openjdk.org/?repo=jdk&pr=26849&range=00
  Issue: https://bugs.openjdk.org/browse/JDK-8347707
  Stats: 195 lines in 46 files changed: 14 ins; 5 del; 176 mod
  Patch: https://git.openjdk.org/jdk/pull/26849.diff
  Fetch: git fetch https://git.openjdk.org/jdk.git pull/26849/head:pull/26849

PR: https://git.openjdk.org/jdk/pull/26849

Reply via email to