glibc/stdio-common/Xprintf_buffer_flush.c
Florian Weimer 659fe9fdd1 stdio-common: Introduce buffers for implementing printf
These buffers will eventually be used instead of FILE * objects
to implement printf functions.  The multibyte buffer is struct
__printf_buffer, the wide buffer is struct __wprintf_buffer.

To enable writing type-generic code, the header files
printf_buffer-char.h and printf_buffer-wchar_t.h define the
Xprintf macro differently, enabling Xprintf (buffer) to stand
for __printf_buffer and __wprintf_buffer as appropriate.  For
common cases, macros like Xprintf_buffer are provided as a more
syntactically convenient shortcut.

Buffer-specific flush callbacks are implemented with a switch
statement instead of a function pointer, to avoid hardening issues
similar to those of libio vtables.  struct __printf_buffer_as_file
is needed to support custom printf specifiers because the public
interface for that requires passing a FILE *, which is why there
is a trapdoor back from these buffers to FILE * streams.

Since the immediate user of these interfaces knows when processing
has finished, there is no flush callback for the end of processing,
only a flush callback for the intermediate buffer flush.

Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
2022-12-19 18:56:54 +01:00

73 lines
2.9 KiB
C

/* Flush wrapper for struct __*printf_buffer. Generic version.
Copyright (C) 2022 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
#include <printf_buffer.h>
#include <assert.h>
#include <stdint.h>
/* Xprintf (buffer_do_flush) (BUF) performs the flush operation. The
actual implementation is specific to the multibyte and wide
variants.
If the flush fails, Xprintf_buffer_mark_failed (BUF) must be
called, and BUF->write_ptr and BUF->write_end can be left
unchanged.
The function must not do anything if failure has already occurred,
that is, if BUF->mode == Xprintf (buffer_mode_failed).
The framework implicitly invokes flush with BUF->write_ptr ==
BUF->write_end only. (This is particularly relevant to the
__sprintf_chk flush, which just calls __chk_fail.) But in some
cases, Xprintf_buffer_flush may be called explicitly (when
BUF->mode/the backing function is known). In that case, it is
possible that BUF->write_ptr < BUF->write_end is true.
If the flush succeeds, the pointers are changed so that
BUF->write_ptr < BUF->write_end. It is possible to switch to a
completely different buffer here. If the buffer is moved, it may
be necessary to updated BUF->write_base and BUF->written from the
flush function as well.
Note that when chaining buffers, in the flush function for the
outer buffer (to which data is written first), it is necessary to
check for BUF->next->failed (for the inner buffer) and set
BUF->base.failed to true (for the outer buffer). This should come
towards the end of the outer flush function. Usually, there is
also some unwrapping step afterwards; it has to check the outer
buffer (BUF->base.failed) and propagate any error to the inner
buffer (BUF->next->failed), so essentially in the other
direction. */
static void Xprintf (buffer_do_flush) (struct Xprintf_buffer *buf);
bool
Xprintf_buffer_flush (struct Xprintf_buffer *buf)
{
if (__glibc_unlikely (Xprintf_buffer_has_failed (buf)))
return false;
Xprintf (buffer_do_flush) (buf);
if (Xprintf_buffer_has_failed (buf))
return false;
/* Ensure that the flush has made available some bytes. */
assert (buf->write_ptr != buf->write_end);
return true;
}