Commit Graph
58 Commits
Author SHA1 Message Date
Armaan Sandhu 7461360c15 utf8: add an ASCII fast path to utf8_is_printable_newline()
This decoded every character twice: utf8_encoded_valid_unichar() decodes
internally to do its validation and then throws the codepoint away, and the
caller immediately called utf8_encoded_to_unichar() again to get the very same
codepoint back.

Fix that where it originates rather than at the call site: add
utf8_encoded_valid_unichar_full(), which optionally returns the character it had
to decode anyway, and make utf8_encoded_valid_unichar() a wrapper that passes
NULL. Callers that want both the validity and the value now decode once.

On top of that add a bulk fast path, since almost everything we are asked about
is plain printable ASCII: test eight bytes at a time for that, i.e. that no byte
is >= 0x80, none is < 0x20 and none is 0x7f. A remainder of less than eight
bytes is copied into a word padded with spaces so that it can be tested the same
way. A word that does not qualify falls through to the per character path, so
tabs, newlines and multibyte sequences are handled exactly as before.

The function sits on journalctl's per-field output path, so this is measurable:
on a 730M journal spread over 100 files, journalctl -o export goes from 4.02s to
about 2.9s.

Also extend the tests, which so far covered neither DEL nor the C1 range nor
allow_newline=false: place each of the interesting bytes at each offset within
the eight byte word, and check a multibyte character straddling the end of one.

The new code was verified to agree with the old one on 41945090 inputs, for both
values of allow_newline: every byte string of length zero to three over all 256
byte values, four million random strings over uniform bytes, over an ASCII-heavy
alphabet and over an alphabet of encoding boundary bytes, and a corpus placing
every byte value at every offset of buffers of every length from eight to
twenty-four bytes, with valid and truncated multibyte sequences straddling the
word boundary.
2026-09-23 08:59:56 +09:00
Armaan Sandhu cd51add717 utf8: make utf8_char_console_width() reject what utf8_is_valid() rejects
utf8_char_console_width() decoded with utf8_encoded_to_unichar(), which does not
check for overlong encodings, does not check unichar_is_valid(), and still
accepts the obsolete five and six byte forms. Those checks live in the wrapper
utf8_encoded_valid_unichar(), which is what utf8_is_valid() and ellipsize_mem()
use. Over all 4244897280 NUL free byte strings of length one to four, that is the
whole UTF-8 encoding space, the wrapper agrees with utf8_is_valid_n() everywhere,
while the bare decoder disagrees on 8835838 of them, always by accepting more.

So utf8_console_width() assigned a finite width to strings that utf8_is_valid()
refuses. format-table.c measures a cell with utf8_console_width() and, if it does
not fit, hands it to ellipsize(), which validates strictly, returns NULL, and is
reported as -ENOMEM. A surrogate, overlong, noncharacter or five byte sequence in
any cell therefore failed the whole table with a bogus out of memory error, and
only once the column happened to be narrow enough to require truncation, so
whether it happened at all depended on the width of the user's terminal. At other
widths the invalid sequence was printed to the terminal instead.

Decode with utf8_encoded_valid_unichar() so that how wide we think a string is
agrees with whether we consider it valid UTF-8 in the first place. Invalid cells
are now refused consistently with -EINVAL, the way the sequences that
utf8_encoded_to_unichar() already rejected always were.

Note that for tables this makes the refusal total rather than per field:
table_data_requested_width_height() turns a SIZE_MAX width into -EINVAL, which
table_format() propagates, so a cell in one of the newly rejected encodings now
fails the whole table at every width. systemd-dissect and repart --dry-run put
GPT partition names read straight off the image into cells without filtering
them, so a crafted partition name suppresses the whole listing where before it
cost one field, and only at some terminal widths. That is the intended trade:
the alternative is to keep emitting raw invalid bytes to the terminal at
whichever widths happen not to need truncation. Should degrading the cell rather
than the table turn out to be preferable, the place for it is where the text
enters the table, via utf8_escape_invalid() and friends, not in a helper whose
job is to say how wide a string is.

This also makes the assert(w >= 0) in utf8_escape_non_printable_full() hold by
construction rather than by the accident that its own validation was stricter
than the width helper's.
2026-09-22 15:58:57 +02:00
Yu Watanabe 5c0d4b5061 utf8: split out unichar_console_width() from utf8_char_console_width()
It will be used later.
2026-07-23 14:40:37 +09:00
Michael Vogt bc4dfc24a5 basic: add assert() when doing pointer deref
Lennart reminded me in [1] that we need to add assert() in functions
that do pointer access. For the simple `*p` pointer dereferences
we even have an automatic coccinelle script that ensures that as
part of the automatic code checks.

However for deref in the `p->` style this is not supported right
now and adding it to coccinelle is hard because its too slow for
this kind of check. So I created a (slightly messy) tree-sitter
python script to see how many asserts we are currently missing.

This commit is the result of running it over the `src/basic`
dir and fixing the flagged issues. I plan to tidy it up and
add it to the checks too but this is orthogonal to this commit.

[1] https://github.com/systemd/systemd/pull/42360#discussion_r3426964562
2026-06-25 10:40:21 +02:00
jmestwa-coder 17260a97c8 fix ellipsis buffer overflow in xescape_full and utf8 escapers 2026-06-17 08:53:55 +02:00
Michael Vogt bd141bd818 many: fix remaining check-pointer-deref issues
The updated parsing_hacks.h file uncovered a bunch of extra
things that the check-pointer-deref coccinelle script flags.

This commit fixes them to make the tree check-pointer-deref clean.
2026-04-12 15:27:15 +02:00
Michael Vogt 18e70eec2f basic: make check-pointer-deref clean
Add the needed assert changes to make the code clean
for the new check-pointer-deref script.

Note that for `_cmp()` functions its a bit of a question
if we want `assert()` or `POINTER_MAY_BE_NULL()` here. The
former is more correct but it adds a (small) runtime hit. If
we use POINTER_MAY_BE_NULL() it is compiled out but strictly
speaking it is wrong (so at least we would need a comment).
2026-03-31 18:30:40 +02:00
Daan De Meyer 0c15577abe basic + fundamental: Clean up includes
Split out of #37344.
2025-05-25 10:06:07 +02:00
Mike Yuan 012658fc85 basic/utf8: add missing assertion
Follow-up for 104a6b8c39
2025-02-21 16:12:59 +01:00
Lennart Poettering 104a6b8c39 utf8: add helper that determines length in bytes of last UTF-8 character in string 2025-02-17 15:05:18 +01:00
Daan De Meyer a07864a4fe bootctl: Add --secure-boot-auto-enroll
When specified, bootctl install will also set up secure boot
auto-enrollment. For now, We sign all variables using the same
certificate and key pair.
2024-11-03 10:46:17 +01:00
Zbigniew Jędrzejewski-Szmek ff3f29537c various: move ptr indicator to return value 2024-06-19 16:37:12 +02:00
Lennart Poettering 9632f8b465 utf8: export utf8_char_console_width() 2024-06-17 09:20:21 +02:00
Mike Yuan 4d06bf5922 basic/utf8: modernize ascii_is_valid_n, make ascii_is_valid static inline 2024-06-16 19:07:35 +02:00
Mike Yuan 7ff7161044 basic/utf8: modernize utf8_is_valid_n a bit 2024-06-16 19:07:34 +02:00
Lennart Poettering bba55185de utf8: let utf8_console_width() handle NULL strings somewhat reasonably 2024-06-15 00:46:42 +01:00
Lennart Poettering b4aaba2bb0 utf8: assume tabs are 8 characters wide when written to console 2024-06-14 21:17:20 +01:00
Yu Watanabe 6952ebae3b tree-wide: drop several remaining license headers
And downgrade the license of utf8.c to LGPL-2.0-or-later, to follow the
original license.
2024-04-08 10:14:50 +02:00
Lennart Poettering ba091282e5 utf8: automatically determine length of string if SIZE_MAX is specified
Let's make utf8_to_utf16() and utf16_to_utf8() a bit nicer to use by
adding shortcuts for common cases.

This is particularly relevant for utf16_to_utf8() since the
multiplication with 2 is easy to forget.
2023-08-24 13:19:12 +02:00
Zbigniew Jędrzejewski-Szmek 9579e9a530 basic/utf8: make utf8_encoded_to_unichar() return length of the codepoint 2023-06-23 17:24:42 -06:00
Zbigniew Jędrzejewski-Szmek 9b49a3b49e basic/utf8: add function to convert to ASCII
The conversion must be lossy because ASCII doesn't have enough chars.
2021-11-16 13:54:38 +01:00
Zbigniew Jędrzejewski-Szmek a456086b37 basic/utf8: inline some iterator variables 2021-11-12 15:51:26 +01:00
Zbigniew Jędrzejewski-Szmek fc96e5c053 basic/escape: allow truncation mode where "…" is always appended
So far we would append "…" or "..." when the string was wider than the specified
output width. But let's add a mode where the caller knows that the string being
passed is already truncated.

The condition for jumping back in utf8_escape_non_printable_full() was
off-by-one. But we only jumped to that label after doing a check with a
stronger condition, so I think it didn't matter. Now it matters because we'd
output the forced ellipsis one column too early.
2021-05-05 13:59:23 +02:00
Zbigniew Jędrzejewski-Szmek 523e1b14a1 basic/string-util: simplify how str_realloc() is used
All callers ignore failure anyway, so let's do that internally.
2021-05-05 12:12:42 +02:00
Yu Watanabe f5fbe71d95 tree-wide: use UINT64_MAX or friends 2021-03-05 07:10:13 +09:00
Susant Sahani fe96c0f86d treewide: tighten variable scope in loops (#18372)
Also use _cleanup_free_ in one more place.
2021-01-27 08:19:39 +01:00
Yu Watanabe db9ecf0501 license: LGPL-2.1+ -> LGPL-2.1-or-later 2020-11-09 13:23:58 +09:00
Zbigniew Jędrzejewski-Szmek 80ab31a435 shared/utf8: add utf8_is_valid_n()
Sometimes we need to check strings without the terminating NUL. Add a variant
that does that.
2020-09-01 16:48:40 +02:00
Zbigniew Jędrzejewski-Szmek 618727dae5 basic/utf8: rename parameter
Every time I was using this function I had to check whether "newline"
means that newlines are good or bad.
2020-08-17 19:48:32 +02:00
Yu Watanabe 455fa9610c tree-wide: drop string.h when string-util.h or friends are included 2019-11-04 00:30:32 +09:00
Zbigniew Jędrzejewski-Szmek 4c6d51390c basic/utf8: reduce memory usage 2019-05-22 10:22:20 +02:00
Zbigniew Jędrzejewski-Szmek da88f542d9 util-lib: add truncation based on printable width to utf8_escape_non_printable() 2019-05-22 09:50:19 +02:00
Zbigniew Jędrzejewski-Szmek 92e068b465 basic/utf8: do not read past end of string when looking for a multi-byte character
Fixes https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=9341.
2019-02-26 12:37:40 +01:00
Zbigniew Jędrzejewski-Szmek 84319aa76e basic/utf8: change type of function to emphasize that it only looks at one character 2019-02-26 12:17:12 +01:00
Lennart Poettering 80b0a59729 utf8: add utf8_to_utf16() helper 2018-09-25 15:57:47 +02:00
Lennart Poettering 2ac2ff3fc1 utf8: let's update utf16_to_utf8() a bit
Let's change utf16_to_utf8() prototype to refer to utf16 chars with char16_t rather than void

Let's not cast away a "const" needlessly.

Let's add a few comments.

Let's fix the calculations of the buffer size to allocate, and how long
to run the loop in case of uneven byte numbers
2018-09-25 15:57:47 +02:00
Lennart Poettering 7c42185784 utf8: change return type of utf8_encoded_expected_len() to size_t
After all it returns a lengths of a string in chars, and hence should
return size_t, exactly like strlen().
2018-09-25 15:57:47 +02:00
Lennart Poettering e71fb4b302 utf8: update utf8_is_valid() a bit
Let's avoid a few casts in the function. Also, let's drop the "const"
when returning the string, for similar reasons as strchr() and friends
drop it: so that we don't add a const if the user passes in a non-const
string.
2018-09-25 15:57:47 +02:00
Zbigniew Jędrzejewski-Szmek d9b02e1697 tree-wide: drop copyright headers from frequent contributors
Fixes #9320.

for p in Shapovalov Chevalier Rozhkov Sievers Mack Herrmann Schmidt Rudenberg Sahani Landden Andersen Watanabe; do
  git grep -e 'Copyright.*'$p -l|xargs perl -i -0pe 's|/([*][*])?[*]\s+([*#]\s+)?Copyright[^\n]*'$p'[^\n]*\s*[*]([*][*])?/\n*|\n|gms; s|\s+([*#]\s+)?Copyright[^\n]*'$p'[^\n]*\n*|\n|gms'
done
2018-06-20 11:58:53 +02:00
Lennart Poettering 96b2fb93c5 tree-wide: beautify remaining copyright statements
Let's unify an beautify our remaining copyright statements, with a
unicode ©. This means our copyright statements are now always formatted
the same way. Yay.
2018-06-14 10:20:21 +02:00
Lennart Poettering 0c69794138 tree-wide: remove Lennart's copyright lines
These lines are generally out-of-date, incomplete and unnecessary. With
SPDX and git repository much more accurate and fine grained information
about licensing and authorship is available, hence let's drop the
per-file copyright notice. Of course, removing copyright lines of others
is problematic, hence this commit only removes my own lines and leaves
all others untouched. It might be nicer if sooner or later those could
go away too, making git the only and accurate source of authorship
information.
2018-06-14 10:20:20 +02:00
Lennart Poettering 818bf54632 tree-wide: drop 'This file is part of systemd' blurb
This part of the copyright blurb stems from the GPL use recommendations:

https://www.gnu.org/licenses/gpl-howto.en.html

The concept appears to originate in times where version control was per
file, instead of per tree, and was a way to glue the files together.
Ultimately, we nowadays don't live in that world anymore, and this
information is entirely useless anyway, as people are very welcome to
copy these files into any projects they like, and they shouldn't have to
change bits that are part of our copyright header for that.

hence, let's just get rid of this old cruft, and shorten our codebase a
bit.
2018-06-14 10:20:20 +02:00
Zbigniew Jędrzejewski-Szmek 294a3121aa basic/utf8: add ascii_is_valid_n() 2018-06-09 13:41:24 +02:00
Lennart Poettering 3f536d5bae utf8: add helper call for counting display width of strings 2018-04-18 12:35:45 +02:00
Zbigniew Jędrzejewski-Szmek 11a1589223 tree-wide: drop license boilerplate
Files which are installed as-is (any .service and other unit files, .conf
files, .policy files, etc), are left as is. My assumption is that SPDX
identifiers are not yet that well known, so it's better to retain the
extended header to avoid any doubt.

I also kept any copyright lines. We can probably remove them, but it'd nice to
obtain explicit acks from all involved authors before doing that.
2018-04-06 18:58:55 +02:00
Lennart Poettering 65ee8660df utf8: add utf8_n_codepoints() for counting complete utf8 codepoints in a string 2018-02-14 18:41:03 +01:00
Zbigniew Jędrzejewski-Szmek 53e1b68390 Add SPDX license identifiers to source files under the LGPL
This follows what the kernel is doing, c.f.
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=5fd54ace4721fc5ce2bb5aef6318fcf17f421460.
2017-11-19 19:08:15 +01:00
Yu Watanabe 4c70109600 tree-wide: use IN_SET macro (#6977) 2017-10-04 16:01:32 +02:00
Vito Caputo 313cefa1d9 tree-wide: make ++/-- usage consistent WRT spacing
Throughout the tree there's spurious use of spaces separating ++ and --
operators from their respective operands.  Make ++ and -- operator
consistent with the majority of existing uses; discard the spaces.
2016-02-22 20:32:04 -08:00
Daniel Mack b26fa1a2fb tree-wide: remove Emacs lines from all files
This should be handled fine now by .dir-locals.el, so need to carry that
stuff in every file.
2016-02-10 13:41:57 +01:00