fix: strip a systemd journal entry's own block padding, not its data - #795
Conversation
|
Cross-review at The branch gates on That is a 100% false-positive rate on that path, not a partial one, and a strict regression against base for those inputs. It also violates the docstring's own rule — these are not the trailing octets of the entry. The fix is one condition, which the reviewer simulated over 12 cases (PR wrong on 3/12, gated version wrong on 0/12, identical on all nine the PR already gets right): if not raw_line.endswith(b'\n') and raw_line.endswith(b'\x00'):It also refuted row 4 of the boundary table I published. I reported What it confirmed, with a stronger argument than the author's. Binary fields really are unreachable, and the structural reason is better: Two prose items also routed: the docstring's "is not reached at all" clause is false (measured — the terminator check is reached and passes), and the PR body's "each new padding-count subtest shown failing" needs "each non-zero", since |
efd38e8 to
5226a03
Compare
|
Revised to So the gate One claim in this PR's body had passed through three hands unverified, so I derived it. The reviewer found that this fix also repairs the rebuild; I relayed it; the worker put it in the body and said honestly that it had taken my word rather than deriving it, since the writer path is outside what it owned. Measured on both trees: Confirmed. On base the alignment padding is baked permanently into the value, so a parse-then-write round trip grows the entry by three octets every time; the fix makes the rebuild match the well-formed form. That is a genuine second benefit and the body's claim stands — but it was asserted by one agent, relayed by me, and published by another without anyone measuring it, which is the chain worth not repeating. Both prose items also landed: the "is not reached at all" clause is corrected (verified directly — New test Re-reviewing at the new head before I set a verdict. |
|
Delta re-check at The new defect: (The reviewer's sweep found 3706 gate-reaching inputs to my 2801 — different alphabet and length bounds, same zero.) That dead arm is exactly the partial branch the report names: Previous head It refuted my own relay of the counts. I wrote that pytest gives 92 and unittest 93, "the counts differ legitimately". Wrong — the previous head was 92/92, this one is 93/93, and adding a method raises both. The PR body's own figure was right; the error was mine. What it confirmed by attacking the new two-condition gate, wider than the test's table: vtab One irreducible cost it re-confirmed rather than reported as new: a NUL tail that is genuinely value data with a true pad of 0 — It also verified the merge state rather than assuming it: Both prose fixes are accurate, with one unflagged exception worth a nit: the terminator check is not reached when the length prefix was over-declared and clamped ( Routing the dead-branch fix and the body's coverage figure. |
A journal entry whose last field lacks the format's mandatory trailing newline let the PCAP-NG block's own 32-bit alignment padding (0-3 NUL octets) be read as part of that field's value, silently -- no exception, no warning. Measured: b'MESSAGE=hello' + 3 padding NULs returned MESSAGE == 'hello\x00\x00\x00'. - The existing padding-only-line guard (#699) only catches padding that lands on a line of its own, which requires the preceding field to have ended with a real newline. Without one, readline() runs straight through the value and into the padding behind it. - Reachable only on text fields: readline() returns a line without its own newline only at end of stream, so a binary field's own length- prefixed value read is never in play, and a binary field *name* landing on such a line fails its own length-prefix read regardless. - Chose the tolerant fix (strip, not refuse): block padding is always 0-3 octets, so at most the trailing 3 octets of a terminator-less line are stripped as padding, with a SchemaWarning; anything past that cannot be padding and is left as data. Gated on the *raw*, unstripped line ending in NUL -- not the whitespace-trimmed one -- since a real trailing newline, or any other raw last octet including ordinary ASCII whitespace, proves the true padding is zero. - Cross-review caught two defects in the first cut. First, gating on raw_line but stripping NULs off line (raw_line.strip()), so a whitespace-tailed, zero-padding entry lost real trailing NULs -- wrong on 3/12 simulated cases, fixed to 0/12. Second, once that gate requires raw_line to end in NUL, bytes.strip() cannot remove it, so the stripping loop always runs and a since-dead `if pad_octets:` guard around the warn+assign was never false; dropped. Closes #794 Test: tests/protocols/misc/test_pcapng_unit.py and tests/integration/test_pcapng_end_to_end.py both green (93/1762 and 6/1/25), cross-checked under python -m unittest; coverage on the changed module 100% (564 stmts, 84 branches, 0 missed); no new mypy or pylint findings against main.
5226a03 to
5777a93
Compare
|
Revised to I had told it to confirm coverage "returns to It also declined to reconcile the body's counts to my retracted relay about 92-vs-93, keeping its own measured The removed guard now carries a comment explaining why it was unreachable, which is better than silently deleting it: the outer gate requires Two extra subtest rows added for vtab Re-review dispatched at this head before I set a verdict. |
|
Delta re-check at The strongest evidence is that it brute-forced the real parser rather than simulating it. 4-aligned bodies over the alphabet So the now-unconditional It also checked the case that would have meant the gate shadowed #699's guard, and it does not: bodies of 4 and 8 bare NULs, and a field followed by a 1-NUL, 3-NUL or And the two new rows earn their place. Reverting the gate to its single-condition form gave Coverage confirms my corrected arithmetic, which I had got wrong the first time: dropping Everything else re-confirmed: 93 / 1762 under both runners, integration 6/1/25, 26 Two residuals, both previously flagged and neither new: the irreducible ambiguity where a terminator-less value genuinely ending in ≤3 NULs on a 4-aligned entry loses them, which no gate can separate since all four padding counts are congruence-consistent; and the docstring's "always reached" not flagging the CI 25✅ / 3⏭ / 0❌ with 2 in flight. Setting |
Please follow the guide below
You will be asked some questions, please read them carefully and answer honestly
Put an
xinto all the boxes [ ] relevant to your pull request (like that [x])Use Preview tab to see how your pull request will actually look like
Searched for similar pull requests
Followed the coding style (
make pylint,make mypy,make isort)make testpasses, and a test case covers the changeAdded a changelog entry under
docs/source/changelog/and regeneratedCHANGELOG.md, if the change is user-visible — N/A — changelog centralised in docs(changelog): shared 1.5.0 changelog — long-lived, merges last (#610, #616, #617, #618, #620) #657What is the purpose of your pull request?
Tick the commit type your subject line carries.
fix— corrects a defectfeat— adds a featureperf— changes performance, not behaviourrefactor— changes neither behaviour nor performancetest— tests onlydocs— documentation onlyci— workflows or build toolingchore— anything elseDescription of your pull request and other information
Chose the tolerant fix (strip the padding), not
ProtocolError: a binary field's valueis read by its own 64-bit length prefix, never by scanning for a line ending, so the
defect is unreachable there. Block alignment padding is always 0-3 octets, so at most
the trailing 3 octets of a terminator-less line are stripped as padding, with a
SchemaWarning— and only when the raw, unstripped line itself ends in NUL. A realtrailing newline, or any other raw last octet (ordinary ASCII whitespace included),
proves the true padding is zero and leaves the line untouched.
Cross-review found two defects in the first cut. First, it gated on
raw_linefor themissing newline but stripped NULs off
line(raw_line.strip()), exposing real dataNULs as padding whenever the entry's actual last octet was ASCII whitespace — wrong on
3/12 simulated cases, fixed to 0/12. Second, once that gate requires
raw_lineto endin NUL,
bytes.strip()can never remove it, so the strip loop always ran and theif pad_octets:guard around the warn+assign was dead code; dropped.Measured:
b'MESSAGE=hello' + b'\x00\x00\x00'(no terminator) returnedMESSAGE == 'hello\x00\x00\x00'before any fix,'hello'plus aSchemaWarningafter.tests/protocols/misc/test_pcapng_unit.py(93/1762) andtests/integration/test_pcapng_end_to_end.py(6/1/25) both green, cross-checked underpython -m unittest. Coverage on the changed module is 100% (564 statements, 84branches, 0 missed). No new mypy or pylint findings against
main. Zero of the 26SchemaWarnings raised across all 23 example fixtures come from this code path.Closes #794