From 231857cfe8da749925ee2beb1352e74fa96f1372 Mon Sep 17 00:00:00 2001 From: dongshengyuan <545258830@qq.com> Date: Tue, 14 Jul 2026 18:52:04 +0800 Subject: [PATCH 1/3] boot: allow BCD fields to end at buffer limit Allow offset + len == max; the range ends exactly at the buffer boundary. Keep the overflow-safe bounds check and cover the edge case in test-bcd. Follow-up for: aa1d0f25873f737fb9306a12f9283872012f2d9a. --- src/boot/bcd.c | 16 +++++++++------- src/boot/test-bcd.c | 9 +++++++++ 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/src/boot/bcd.c b/src/boot/bcd.c index 4533d479c1..67736d9483 100644 --- a/src/boot/bcd.c +++ b/src/boot/bcd.c @@ -93,15 +93,17 @@ assert_cc(offsetof(KeyValue, data_offset) == 8); assert_cc(offsetof(KeyValue, data_type) == 12); assert_cc(offsetof(KeyValue, name) == 20); -#define BAD_OFFSET(offset, len, max) \ - ((uint64_t) (offset) + (len) >= (max)) +static bool bad_offset(uint64_t offset, uint64_t len, uint64_t max) { + return offset > max || len > max - offset; +} #define BAD_STRUCT(type, offset, max) \ - ((uint64_t) (offset) + sizeof(type) >= (max)) + bad_offset(offset, sizeof(type), max) #define BAD_ARRAY(type, array, offset, array_len, max) \ - ((uint64_t) (offset) + offsetof(type, array) + \ - sizeof((type){}.array[0]) * (uint64_t) (array_len) >= (max)) + bad_offset(offset, \ + offsetof(type, array) + sizeof((type){}.array[0]) * (uint64_t) (array_len), \ + max) static const Key *get_key(const uint8_t *bcd, uint32_t bcd_len, uint32_t offset, const char *name); @@ -167,7 +169,7 @@ static const KeyValue *get_key_value(const uint8_t *bcd, uint32_t bcd_len, const if (key->n_key_values == 0) return NULL; - if (BAD_OFFSET(key->key_values_offset, sizeof(uint32_t) * (uint64_t) key->n_key_values, bcd_len) || + if (bad_offset(key->key_values_offset, sizeof(uint32_t) * (uint64_t) key->n_key_values, bcd_len) || (uintptr_t) (bcd + key->key_values_offset) % alignof(uint32_t) != 0) return NULL; @@ -191,7 +193,7 @@ static const KeyValue *get_key_value(const uint8_t *bcd, uint32_t bcd_len, const if (FLAGS_SET(kv->data_size, UINT32_C(1) << 31)) continue; - if (BAD_OFFSET(kv->data_offset, kv->data_size, bcd_len)) + if (bad_offset(kv->data_offset, kv->data_size, bcd_len)) continue; if (strncaseeq8(name, kv->name, kv->name_len) && strlen8(name) == kv->name_len) diff --git a/src/boot/test-bcd.c b/src/boot/test-bcd.c index 27102c236b..d6e307b6ec 100644 --- a/src/boot/test-bcd.c +++ b/src/boot/test-bcd.c @@ -90,6 +90,15 @@ TEST(base_block) { *bcd = backup; } +TEST(offset_bounds) { + assert_se(!bad_offset(0, sizeof(Key), sizeof(Key))); + assert_se(!BAD_STRUCT(Key, 0, sizeof(Key))); + assert_se(!BAD_ARRAY(Key, key_name, 0, 0, sizeof(Key))); + + assert_se(bad_offset(0, sizeof(Key) + 1, sizeof(Key))); + assert_se(bad_offset(UINT32_MAX, 2, UINT32_MAX)); +} + TEST(bad_bcd) { size_t len; uint8_t *hbins; From e72d9b0d7068bb7de494847c20044e073e0d6979 Mon Sep 17 00:00:00 2001 From: dongshengyuan <545258830@qq.com> Date: Tue, 14 Jul 2026 18:52:26 +0800 Subject: [PATCH 2/3] id128: honor json output for single ids JSON mode was accepted by single-ID verbs but still printed bare IDs. Print JSON objects for those verbs and reject ambiguous JSON combinations. Follow-up for: a50666e376057a24b67f80c9a8025096c750fb23. --- src/id128/id128.c | 49 +++++++++++++++++++++++---- test/units/TEST-74-AUX-UTILS.id128.sh | 6 ++++ 2 files changed, 49 insertions(+), 6 deletions(-) diff --git a/src/id128/id128.c b/src/id128/id128.c index e66acd33e5..a4452f1cc5 100644 --- a/src/id128/id128.c +++ b/src/id128/id128.c @@ -2,6 +2,8 @@ #include +#include "sd-json.h" + #include "alloc-util.h" #include "build.h" #include "format-table.h" @@ -24,9 +26,37 @@ static PagerFlags arg_pager_flags = 0; static bool arg_legend = true; static sd_json_format_flags_t arg_json_format_flags = SD_JSON_FORMAT_OFF; +static int print_id(sd_id128_t id) { + _cleanup_(sd_json_variant_unrefp) sd_json_variant *json = NULL; + int r; + + if (!sd_json_format_enabled(arg_json_format_flags)) + return id128_pretty_print(id, arg_mode); + + if (arg_mode == ID128_PRINT_ID128) + r = sd_json_buildo(&json, SD_JSON_BUILD_PAIR_ID128("id", id)); + else + r = sd_json_buildo(&json, SD_JSON_BUILD_PAIR_UUID("id", id)); + if (r < 0) + return log_error_errno(r, "Failed to create JSON: %m"); + + r = sd_json_variant_dump(json, arg_json_format_flags, stdout, NULL); + if (r < 0) + return log_error_errno(r, "Failed to print JSON: %m"); + + return 0; +} + VERB_NOARG(verb_new, "new", "Generate a new ID"); static int verb_new(int argc, char *argv[], uintptr_t _data, void *userdata) { - return id128_print_new(arg_mode); + sd_id128_t id; + int r; + + r = sd_id128_randomize(&id); + if (r < 0) + return log_error_errno(r, "Failed to generate ID: %m"); + + return print_id(id); } VERB_NOARG(verb_machine_id, "machine-id", "Print the ID of current machine"); @@ -42,7 +72,7 @@ static int verb_machine_id(int argc, char *argv[], uintptr_t _data, void *userda return log_error_errno(r, "Failed to get %smachine-ID: %m", sd_id128_is_null(arg_app) ? "" : "app-specific "); - return id128_pretty_print(id, arg_mode); + return print_id(id); } VERB_NOARG(verb_boot_id, "boot-id", "Print the ID of current boot"); @@ -58,7 +88,7 @@ static int verb_boot_id(int argc, char *argv[], uintptr_t _data, void *userdata) return log_error_errno(r, "Failed to get %sboot-ID: %m", sd_id128_is_null(arg_app) ? "" : "app-specific "); - return id128_pretty_print(id, arg_mode); + return print_id(id); } VERB_NOARG(verb_invocation_id, "invocation-id", "Print the ID of current invocation"); @@ -74,7 +104,7 @@ static int verb_invocation_id(int argc, char *argv[], uintptr_t _data, void *use if (r < 0) return log_error_errno(r, "Failed to get invocation-ID: %m"); - return id128_pretty_print(id, arg_mode); + return print_id(id); } VERB_NOARG(verb_var_uuid, "var-partition-uuid", "Print the UUID for the /var/ partition"); @@ -91,7 +121,7 @@ static int verb_var_uuid(int argc, char *argv[], uintptr_t _data, void *userdata if (r < 0) return log_error_errno(r, "Failed to generate machine-specific /var/ UUID: %m"); - return id128_pretty_print(id, arg_mode); + return print_id(id); } static int show_one(Table **table, const char *name, sd_id128_t uuid, bool first) { @@ -120,7 +150,7 @@ static int show_one(Table **table, const char *name, sd_id128_t uuid, bool first } if (arg_value) - return id128_pretty_print(uuid, arg_mode); + return print_id(uuid); if (!*table) { *table = table_new("name", "id"); @@ -141,6 +171,10 @@ static int verb_show(int argc, char *argv[], uintptr_t _data, void *userdata) { int r; argv = strv_skip(argv, 1); + if (arg_value && sd_json_format_enabled(arg_json_format_flags) && strv_length(argv) != 1) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "'show --value --json=' requires exactly one argument."); + if (argv) STRV_FOREACH(p, argv) { sd_id128_t uuid; @@ -290,6 +324,9 @@ static int parse_argv(int argc, char *argv[], char ***ret_args) { break; } + if (arg_mode == ID128_PRINT_PRETTY && sd_json_format_enabled(arg_json_format_flags)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--pretty cannot be combined with --json=."); + *ret_args = option_parser_get_args(&opts); return 1; } diff --git a/test/units/TEST-74-AUX-UTILS.id128.sh b/test/units/TEST-74-AUX-UTILS.id128.sh index b9db1f1454..5da2323461 100755 --- a/test/units/TEST-74-AUX-UTILS.id128.sh +++ b/test/units/TEST-74-AUX-UTILS.id128.sh @@ -28,21 +28,25 @@ systemd-id128 show --json=short systemd-id128 show --no-legend systemd-id128 show --no-pager --no-legend systemd-id128 show root -P -u +systemd-id128 show root --value --json=short | jq -e '.id | type == "string"' [[ -n "$(systemd-id128 var-partition-uuid)" ]] [[ "$(systemd-id128 var-partition-uuid)" != "4d21b016b53445c2a9fb5c16e091fd2d" ]] [[ "$(systemd-id128 new | wc -c)" -eq 33 ]] +systemd-id128 --json=short new | jq -e '.id | type == "string"' systemd-id128 new -p systemd-id128 new -u systemd-id128 new -a 4f68bce3e8cd4db196e7fbcaf984b709 systemd-id128 machine-id +assert_eq "$(systemd-id128 --json=short machine-id | jq --raw-output .id)" "$( Date: Tue, 14 Jul 2026 18:52:52 +0800 Subject: [PATCH 3/3] sysupdate: print version details once list VERSION printed the version status header twice in plain output. Keep the complete later header and add a regression check. Follow-up for: 42c0b689a800b2ec7cceb1528d3834bf9b3417f8. --- src/sysupdate/sysupdate.c | 15 --------------- test/units/TEST-72-SYSUPDATE.sh | 1 + 2 files changed, 1 insertion(+), 15 deletions(-) diff --git a/src/sysupdate/sysupdate.c b/src/sysupdate/sysupdate.c index 01b4a03f4d..9febc9c9c7 100644 --- a/src/sysupdate/sysupdate.c +++ b/src/sysupdate/sysupdate.c @@ -812,21 +812,6 @@ static int context_show_version(Context *c, const char *version) { if (arg_json_format_flags & (SD_JSON_FORMAT_OFF|SD_JSON_FORMAT_PRETTY|SD_JSON_FORMAT_PRETTY_AUTO)) pager_open(arg_pager_flags); - if (!sd_json_format_enabled(arg_json_format_flags)) - printf("%s%s%s Version: %s\n" - " State: %s%s%s\n" - "Installed: %s%s%s%s\n" - "Available: %s%s\n" - "Protected: %s%s%s\n" - " Obsolete: %s%s%s\n\n", - strempty(update_set_flags_to_color(us->flags)), update_set_flags_to_glyph(us->flags), ansi_normal(), us->version, - strempty(update_set_flags_to_color(us->flags)), update_set_flags_to_string(us->flags), ansi_normal(), - yes_no(us->flags & UPDATE_INSTALLED), FLAGS_SET(us->flags, UPDATE_INSTALLED|UPDATE_NEWEST) ? " (newest)" : "", - FLAGS_SET(us->flags, UPDATE_INSTALLED|UPDATE_PENDING) ? " (pending)" : "", FLAGS_SET(us->flags, UPDATE_INSTALLED|UPDATE_PARTIAL) ? " (partial)" : "", - yes_no(us->flags & UPDATE_AVAILABLE), (us->flags & (UPDATE_INSTALLED|UPDATE_AVAILABLE|UPDATE_NEWEST)) == (UPDATE_AVAILABLE|UPDATE_NEWEST) ? " (newest)" : "", - FLAGS_SET(us->flags, UPDATE_INSTALLED|UPDATE_PROTECTED) ? ansi_highlight() : "", yes_no(FLAGS_SET(us->flags, UPDATE_INSTALLED|UPDATE_PROTECTED)), ansi_normal(), - us->flags & UPDATE_OBSOLETE ? ansi_highlight_red() : "", yes_no(us->flags & UPDATE_OBSOLETE), ansi_normal()); - t = table_new("type", "path", "ptuuid", "ptflags", "mtime", "mode", "size", "tries-done", "tries-left", "noauto", "ro", "growfs", "sha256"); if (!t) return log_oom(); diff --git a/test/units/TEST-72-SYSUPDATE.sh b/test/units/TEST-72-SYSUPDATE.sh index d3a8a4bc61..ba2636ba46 100755 --- a/test/units/TEST-72-SYSUPDATE.sh +++ b/test/units/TEST-72-SYSUPDATE.sh @@ -1215,6 +1215,7 @@ InstancesMax=1 EOF "$SYSUPDATE" --verify=no update cmp "$WORKDIR/source/sub/blob-v11.bin" "$WORKDIR/blobs/blob-v11.bin" +[[ "$("$SYSUPDATE" --verify=no list v11 | grep -c "Version: v11")" == "1" ]] rm "$CONFIGDIR/01-basename-dir.transfer" # A url-file source pulled via file:// using SHA256SUMS with "sub/blob-v1x.bin" entries