Skip to content

percent-util: refuse negative values with a zero integer part - #44652

Open
b84330808 wants to merge 3 commits into
systemd:mainfrom
b84330808:fix/percent-util-negative-fraction
Open

b84330808 wants to merge 3 commits into
systemd:mainfrom
b84330808:fix/percent-util-negative-fraction

Conversation

@b84330808

Copy link
Copy Markdown
Contributor

The percent, permille and permyriad parsers parse the integer part with safe_atoi() and refuse negative results, but safe_atoi() parses "-0" as 0, so a negative value with a zero integer part slipped through and its sign was lost once the fractional part was added: parse_permyriad("-0.5%") returned 50, while "-7%" is refused with ERANGE. As a result, CPUQuota=-0.5% was silently applied as a 0.5% quota. The first commit refuses a "-" in the integer part explicitly; this also refuses "-0%" itself, consistent with parse_time(). The second commit fixes the same hole in parse_loadavg_fixed_point(), where "-0.50" was accepted as 0.50.

I ran the tests locally on Linux: the new cases in test-percent-util and test-parse-util fail without this change and pass with it, and test-conf-parser and test-psi-util still pass.

Disclosure: this patch was prepared with AI assistance. I reviewed every line myself and ran the tests above.

@github-actions github-actions Bot added tests util-lib please-review PR is ready for (re-)review by a maintainer labels Nov 5, 2026
@github-actions

github-actions Bot commented Nov 5, 2026 •

Copy link
Copy Markdown

Reviewed 2f6a645 (architecture, correctness, security, style, tests; failed: lifetimes).

Suggestions

  • src/basic/parse-util.c:822 Rather than scanning s for '-' after parsing, pass 10 | SAFE_ATO_REFUSE_PLUS_MINUS (plus SAFE_ATO_REFUSE_LEADING_WHITESPACE for f_str) to both safe_atolu_full() calls. That flag is the usual way to refuse a sign, and it also refuses "1.+5" and "1. 5". It returns EINVAL, so the "-4000.5" test needs adjusting. (outdated)
  • src/basic/parse-util.c:811 ed26871 is a review fixup that removes the strchr() scan f8e6416 just added. Please squash it into f8e6416. As it stands, that commit's message ("Refuse the '-' explicitly, like parse_parts_value_*() in percent-util now do") describes code that no longer exists.

The review is incomplete, so the next push reviews the full diff again.

Workflow run

Comment thread src/basic/parse-util.c Outdated
Comment on lines +819 to +822
/* safe_atolu_full() accepts "-0", hence refuse the "-" explicitly, so that e.g. "-0.50" is not
* taken as 0.50. */
if (strchr(s, '-'))
return -ERANGE;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude: suggestion: Rather than scanning s for '-' after parsing, pass 10 | SAFE_ATO_REFUSE_PLUS_MINUS (plus SAFE_ATO_REFUSE_LEADING_WHITESPACE for f_str) to both safe_atolu_full() calls. That flag is the usual way to refuse a sign, and it also refuses "1.+5" and "1. 5". It returns EINVAL, so the "-4000.5" test needs adjusting.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, done in ed26871: both calls now pass 10 | SAFE_ATO_REFUSE_PLUS_MINUS (the fraction also SAFE_ATO_REFUSE_LEADING_WHITESPACE) and the strchr() scan is gone. This also refuses "+1.5", "1.+5" and "1. 5", which the scan let through. The negative cases now return -EINVAL, so the test expectations are adjusted and the new refusals are covered by tests.

Comment thread src/basic/parse-util.c
f_str = d + 1;

r = safe_atolu_full(i_str, 10, &i);
r = safe_atolu_full(i_str, 10 | SAFE_ATO_REFUSE_PLUS_MINUS, &i);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude: suggestion: ed26871 is a review fixup that removes the strchr() scan f8e6416 just added. Please squash it into f8e6416. As it stands, that commit's message ("Refuse the '-' explicitly, like parse_parts_value_*() in percent-util now do") describes code that no longer exists.

@yuwata

yuwata commented Nov 6, 2026

Copy link
Copy Markdown
Member

Please rebase to fix the Lint Code Base failure. Also, see the comment by Claude.

@yuwata yuwata added reviewed/needs-rework 🔨 PR has been reviewed and needs another round of reworks needs-rebase and removed please-review PR is ready for (re-)review by a maintainer labels Nov 6, 2026
The percent/permille/permyriad parsers parse the integer part with
safe_atoi() and then refuse negative results. However, safe_atoi()
parses "-0" as 0, hence a negative value whose integer part is zero
slipped through, and the sign got lost when the fractional part was
added. E.g. parse_permyriad("-0.5%") returned 50 (i.e. 0.5%), while
parse_permyriad("-7%") correctly failed with ERANGE. As a result,
settings such as CPUQuota=-0.5% or ManagedOOMMemoryPressureLimit=-0.5%
were silently accepted as positive values.

Let's refuse a "-" in the integer part explicitly, so that all
negative values are rejected with ERANGE.
parse_loadavg_fixed_point() parses both parts of the value with
safe_atolu_full(), which refuses negative numbers with ERANGE, except
for "-0". Hence "-4000.5" was refused, but "-0.50" was accepted as 0.50
and "1.-0" as 1.00.

Pass SAFE_ATO_REFUSE_PLUS_MINUS to both calls, plus
SAFE_ATO_REFUSE_LEADING_WHITESPACE for the fractional part, so that any
sign is refused up front. This also refuses "+1.5", "1.+5" and "1. 5".
Negative values now fail with EINVAL, as documented for the flag, and
the test expectations are adjusted accordingly.
@b84330808
b84330808 force-pushed the fix/percent-util-negative-fraction branch from ed26871 to c5e7f0f Compare November 6, 2026 09:22
@b84330808

Copy link
Copy Markdown
Contributor Author

Rebased onto current main and squashed the follow-up into the parse-util commit; the commit message now describes the SAFE_ATO_REFUSE_PLUS_MINUS approach.

@github-actions github-actions Bot added please-review PR is ready for (re-)review by a maintainer and removed reviewed/needs-rework 🔨 PR has been reviewed and needs another round of reworks needs-rebase labels Nov 6, 2026
@poettering

poettering commented Nov 6, 2026 •

Copy link
Copy Markdown
Member

one of those days we should just stop use ato*l() altogether. we are adjusting so much around it, that the question is why not just do the thing entirely on our own...

(this is not a request to implement any patch to get rid of ato*l() use, it's just a general comment)

Comment thread src/basic/percent-util.c Outdated
return -EINVAL;

n = strndupa_safe(p, pc - p);
r = safe_atoi(n, &v);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not just use safe_atoi_full(n, 10 | SAFE_ATO_REFUSE_PLUS_MINUS | SAFE_ATO_REFUSE_LEADING_ZERO, v) here?

i mean, that'd be more correct anyway, since we do not want to allow hex or oct here...

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 2f6a645. One adjustment: there is no safe_atoi_full() in the tree (only safe_atoi(), which takes no flags), so I used safe_atou_full() with the base and flags you suggested (10 | SAFE_ATO_REFUSE_PLUS_MINUS | SAFE_ATO_REFUSE_LEADING_ZERO), plus an INT_MAX check to keep the previous range behavior. Hex/octal spellings and leading zeros are rejected now too. One visible consequence: signed inputs now fail with -EINVAL instead of -ERANGE, and I updated the test expectations accordingly. test-percent-util and test-parse-util pass.

@poettering poettering added reviewed/needs-rework 🔨 PR has been reviewed and needs another round of reworks and removed please-review PR is ready for (re-)review by a maintainer labels Nov 6, 2026
…part

Refuse signs, leading zeros and non-decimal spellings (hex/octal) by
parsing the integer part with safe_atou_full() in base 10 with
SAFE_ATO_REFUSE_PLUS_MINUS|SAFE_ATO_REFUSE_LEADING_ZERO, as suggested
in review. Signed inputs now fail with -EINVAL instead of -ERANGE;
update the test expectations accordingly.
@github-actions github-actions Bot added please-review PR is ready for (re-)review by a maintainer and removed reviewed/needs-rework 🔨 PR has been reviewed and needs another round of reworks labels Nov 6, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

claude-review please-review PR is ready for (re-)review by a maintainer tests util-lib

Development

Successfully merging this pull request may close these issues.

4 participants