Skip to content

htsAddLink walks back from an empty codebase, one byte before the buffer#767

Merged
xroche merged 3 commits into
masterfrom
fix/htsaddlink-730
Jul 27, 2026
Merged

htsAddLink walks back from an empty codebase, one byte before the buffer#767
xroche merged 3 commits into
masterfrom
fix/htsaddlink-730

Conversation

@xroche

@xroche xroche commented Jul 27, 2026

Copy link
Copy Markdown
Owner

Same idiom as the lienrelatif() underflow fixed in #729. The trim that walks back to the last / starts at codebase + strlen(codebase) - 1, which is codebase - 1 when the string is empty, and the loop dereferences it before a > codebase can stop it.

Unlike #729 there is no reachable empty value, and I checked rather than assumed: codebase is copied from a recorded link's fil, and no hts_record_link() call site can supply an empty one. Every fil is either a literal seed or an ident_url_absolute() / ident_url_relatif() success return, and fil_simplifie() restores "/" or "./" rather than leaving a path empty. A probe over 37 hostile URL shapes produced no empty result, with a seeded empty control to show the probe could see one.

The guard goes in anyway, and -#test=addlink drives the walk directly, so this is a regression net rather than a characterization test: in the sanitize job's ASan+UBSan build the test fails on the unfixed walk and passes with the guard. Two further cases pin the ordinary trim, which the guard leaves alone. One of them uses a query-only link, because that is the shape where the trim matters at all: for an ordinary relative link ident_url_relatif() re-derives the directory itself, and deleting the trim outright goes unnoticed.

Closes #730

xroche and others added 3 commits July 27, 2026 09:10
Same idiom as the lienrelatif() underflow fixed in #729: the trim that walks
back to the last '/' starts at codebase + strlen(codebase) - 1, which is
codebase - 1 when the string is empty, and the loop dereferences it before
a > codebase stops it.

Unlike #729 there is no reachable empty value. codebase is copied from a
recorded link's fil, and no hts_record_link() call site can supply an empty
one: every fil is either a literal seed or an ident_url_absolute() /
ident_url_relatif() success return, and fil_simplifie() restores "/" or "./"
rather than leaving a path empty. The guard goes in anyway, and -#test=addlink
drives the walk directly: under the sanitize job's ASan+UBSan build the test
fails on the unfixed walk and passes with the guard. Its second case pins the
ordinary trim, which the guard leaves alone.

Signed-off-by: Xavier Roche <roche@httrack.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Xavier Roche <roche@httrack.com>
For an ordinary relative link ident_url_relatif() re-derives the directory
from the path it is handed, so deleting the trim outright left the two
existing cases green. A query-only link ("?x=1") copies that path whole, and
does catch it.

Signed-off-by: Xavier Roche <roche@httrack.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Xavier Roche <roche@httrack.com>
@xroche
xroche merged commit 5576581 into master Jul 27, 2026
22 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

htsAddLink walks back from an empty codebase, one byte before the buffer

1 participant