Re: [PATCH] tools api io: Return high-bit bytes as unsigned characters

From: Ian Rogers

Date: Thu Nov 12 2026 - 13:00:04 EST


On Thu, Nov 12, 2026 at 9:06 AM Wojciech Szpucha <szpuszi@xxxxxxxxx> wrote:
>
> From: Wojciech Szpucha <szpuszi@xxxxxxxxx>
>
> On signed-char builds, io__get_char() returns negative values for bytes
> 0x80-0xff. This makes filename__read_str() silently truncate non-ASCII
> input in standalone libapi builds.
>
> Cast the byte to unsigned char before returning it as int. Add regression
> tests compiled with -fsigned-char, since perf defaults to -funsigned-char
> and already avoids the issue.
>
> Fixes: e95770af4c4a ("tools api: Add a lightweight buffered reading api")
> Signed-off-by: Wojciech Szpucha <szpuszi@xxxxxxxxx>

Can you provide more context of where this is a problem? The code
builds with -funsigned-char :
https://lore.kernel.org/linux-perf-users/20260306191908.2065682-1-irogers@xxxxxxxxxx/
and so we may be failing to pass CFLAGS through properly for the libapi build.

Thanks,
Ian

> ---
> Tested on Ubuntu 24.04 x86_64, GCC 13.3.0: standalone libapi reads 0/4
> bytes before the fix and 4/4 afterwards. The API IO regression fails
> before and passes after. libapi and perf build successfully; checkpatch
> passes. Full perf suite and kernel builds were not run.
>
> https://github.com/szpuszi/linux-perf-io-test/actions/runs/43257707229
>
> tools/lib/api/io.h | 2 +-
> tools/perf/tests/Build | 1 +
> tools/perf/tests/api-io.c | 33 ++++++++++++++++++++++++++++++++-
> 3 files changed, 34 insertions(+), 2 deletions(-)
>
> diff --git a/tools/lib/api/io.h b/tools/lib/api/io.h
> index 1731996b..25e2008d 100644
> --- a/tools/lib/api/io.h
> +++ b/tools/lib/api/io.h
> @@ -91,7 +91,7 @@ static inline int io__get_char(struct io *io)
> if (ret)
> return ret;
> }
> - return *io->data++;
> + return (unsigned char)*io->data++;
> }
>
> /* Read a hexadecimal value with no 0x prefix into the out argument hex. If the
> diff --git a/tools/perf/tests/Build b/tools/perf/tests/Build
> index 58858f3d..c3ba1fca 100644
> --- a/tools/perf/tests/Build
> +++ b/tools/perf/tests/Build
> @@ -79,6 +79,7 @@ perf-test-$(CONFIG_DWARF_UNWIND) += dwarf-unwind.o
> endif
>
> CFLAGS_dwarf-unwind.o += -fno-optimize-sibling-calls
> +CFLAGS_api-io.o += -fsigned-char
>
> perf-test-y += workloads/
>
> diff --git a/tools/perf/tests/api-io.c b/tools/perf/tests/api-io.c
> index 0ba3d5cc..f283712e 100644
> --- a/tools/perf/tests/api-io.c
> +++ b/tools/perf/tests/api-io.c
> @@ -98,7 +98,7 @@ static int do_test_get_char(const char *test_string,
> size_t buf_size)
> for (i = 0; i < strlen(test_string); i++) {
> ch = io__get_char(&io);
>
> - EXPECT_EQUAL(ch, test_string[i]);
> + EXPECT_EQUAL(ch, (unsigned char)test_string[i]);
> EXPECT_EQUAL(io.eof, false);
> }
> ch = io__get_char(&io);
> @@ -118,6 +118,7 @@ static int test_get_char(void)
> "12345678abcdef90",
> "a\nb\nc\nd\n",
> "\a\b\t\v\f\r",
> + "\x7f\x80\xfe\xff",
> };
> for (i = 0; i <= 10; i++) {
> for (j = 0; j < ARRAY_SIZE(test_strings); j++) {
> @@ -290,6 +291,34 @@ static int test_get_dec(void)
> return ret;
> }
>
> +static int test_get_line_non_ascii(void)
> +{
> + static const char test_string[] = "a\xc3\xa9\xff\nnext\n";
> + char path[PATH_MAX];
> + struct io io;
> + char *line = NULL;
> + size_t line_len = 0;
> + int len, ret = 0;
> +
> + if (setup_test(path, test_string, 2, &io))
> + return -1;
> +
> + len = io__getline(&io, &line, &line_len);
> + EXPECT_EQUAL(len, 5);
> + EXPECT_EQUAL((int)line_len, 5);
> + EXPECT_EQUAL((int)strlen(line), 5);
> + if (line_len == 5)
> + EXPECT_EQUAL(memcmp(line, test_string, 5), 0);
> + len = io__getline(&io, &line, &line_len);
> + EXPECT_EQUAL(len, 5);
> + EXPECT_EQUAL((int)line_len, 5);
> + EXPECT_EQUAL(strcmp(line, "next\n"), 0);
> +
> + free(line);
> + cleanup_test(path, &io);
> + return ret;
> +}
> +
> static int test_get_line(void)
> {
> char path[PATH_MAX];
> @@ -337,6 +366,8 @@ static int test__api_io(struct test_suite *test
> __maybe_unused,
> ret = TEST_FAIL;
> if (test_get_line())
> ret = TEST_FAIL;
> + if (test_get_line_non_ascii())
> + ret = TEST_FAIL;
> return ret;
> }
>
>
> base-commit: 9d66f26fefacebfeed15de31c1123451a17ff119