What
hugepages_pool_ceiling_mb (#398, new in v1.16.0) accepts a value with more digits than bash can
evaluate as an integer, and then silently applies no ceiling at all. The key exists to be a hard
cap; in this case it is a declaration that reads as a guard and enforces nothing.
This is the same defect class as #405 — [ "$X" -lt 1 ] returning 2 rather than false — in a new
key. v1.16.0 was tagged before #405's fix landed, so the lesson had not yet reached this code.
The chain, both halves
Validation accepts it. rigforge.sh, parse_config:
if ! [[ "$HUGEPAGES_POOL_CEILING_MB" =~ ^[0-9]+$ ]] || [ "$HUGEPAGES_POOL_CEILING_MB" -gt 65536 ]; then
An all-digits value passes the regex, so the || falls through to [ -gt 65536 ], which returns
2 — not false — on a value bash cannot evaluate. The if takes its else branch and no error
fires. The only tell is a raw [: 99999999999999999999: integer expression expected on stderr.
Then the cap is skipped, silently. _ensure_hugepages:
if [ "${HUGEPAGES_POOL_CEILING_MB:-0}" -gt 0 ] 2>/dev/null; then
The same evaluation fails the same way, and because this one does redirect stderr, the ceiling
block is skipped with no message whatsoever. The write proceeds at the uncapped
current + required - avail.
Reproduction
Executed against the exact predicate shapes, not inferred:
input: 65537 -> REJECTED (correct)
input: 99999999999999999999 -> ACCEPTED (wrong; stderr: integer expression expected)
then _ensure_hugepages: -> ceiling block SKIPPED, no cap applied
Severity, stated honestly
Fail-open to today's behaviour, not to something new. A rig with a garbage ceiling reserves
exactly what it would have reserved with the key absent, so nothing regresses relative to v1.15.x,
and this does not block the appliance pin bump (pithead#1407).
What it costs is the key's whole purpose. The ceiling is the fix for a RAM-constrained co-resident
box that cannot absorb the over-reserve (#398 / pithead#1103, an ~12 GiB request on an 8 GiB box). An
operator who fat-fingers the value believes that box is capped and it is not.
Suggested fix
Bound the digit length before the numeric comparison, the way #406 fixed the port guard — key on
length first, so the comparison only ever sees a value bash can evaluate. 65536 is a five-digit
ceiling, so anything past six digits is out of range by inspection and needs no arithmetic.
Worth doing in the same pass: _ensure_hugepages's 2>/dev/null hides the second failure. Once
validation is airtight that guard can only see a validated value, but a defensive error there
rather than a silent skip would mean the cap can never be dropped in silence.
Coverage
An assertion that a too-long ceiling is REJECTED, plus a control asserting a valid ceiling is still
accepted and still caps. The existing #405/#408 pool-key assertions in tests/run.sh are the
pattern — each names its own kill so a mutation table stays readable.
What
hugepages_pool_ceiling_mb(#398, new in v1.16.0) accepts a value with more digits than bash canevaluate as an integer, and then silently applies no ceiling at all. The key exists to be a hard
cap; in this case it is a declaration that reads as a guard and enforces nothing.
This is the same defect class as #405 —
[ "$X" -lt 1 ]returning 2 rather than false — in a newkey. v1.16.0 was tagged before #405's fix landed, so the lesson had not yet reached this code.
The chain, both halves
Validation accepts it.
rigforge.sh,parse_config:An all-digits value passes the regex, so the
||falls through to[ -gt 65536 ], which returns2 — not false — on a value bash cannot evaluate. The
iftakes its else branch and no errorfires. The only tell is a raw
[: 99999999999999999999: integer expression expectedon stderr.Then the cap is skipped, silently.
_ensure_hugepages:The same evaluation fails the same way, and because this one does redirect stderr, the ceiling
block is skipped with no message whatsoever. The write proceeds at the uncapped
current + required - avail.Reproduction
Executed against the exact predicate shapes, not inferred:
Severity, stated honestly
Fail-open to today's behaviour, not to something new. A rig with a garbage ceiling reserves
exactly what it would have reserved with the key absent, so nothing regresses relative to v1.15.x,
and this does not block the appliance pin bump (pithead#1407).
What it costs is the key's whole purpose. The ceiling is the fix for a RAM-constrained co-resident
box that cannot absorb the over-reserve (#398 / pithead#1103, an ~12 GiB request on an 8 GiB box). An
operator who fat-fingers the value believes that box is capped and it is not.
Suggested fix
Bound the digit length before the numeric comparison, the way #406 fixed the port guard — key on
length first, so the comparison only ever sees a value bash can evaluate.
65536is a five-digitceiling, so anything past six digits is out of range by inspection and needs no arithmetic.
Worth doing in the same pass:
_ensure_hugepages's2>/dev/nullhides the second failure. Oncevalidation is airtight that guard can only see a validated value, but a defensive
errorthererather than a silent skip would mean the cap can never be dropped in silence.
Coverage
An assertion that a too-long ceiling is REJECTED, plus a control asserting a valid ceiling is still
accepted and still caps. The existing #405/#408 pool-key assertions in
tests/run.share thepattern — each names its own kill so a mutation table stays readable.