compare file names over their full length, including bytes after embedded NUL by ManoharPaturi · Pull Request #607 · nih-at/libzip · GitHub
Skip to content

compare file names over their full length, including bytes after embedded NUL - #607

Open
ManoharPaturi wants to merge 1 commit into
nih-at:mainfrom
ManoharPaturi:pr-hash-nul-name
Open

ManoharPaturi wants to merge 1 commit into
nih-at:mainfrom
ManoharPaturi:pr-hash-nul-name

Conversation

@ManoharPaturi

Copy link
Copy Markdown
Contributor

Problem

File names inside an archive are byte strings with an explicit length, and they may contain embedded NUL bytes. The name hash table treated them as C strings instead: siphash hashed up to the first NUL, and lookups compared names with strcmp. Two consequences for archives opened without ZIP_CHECKCONS, which accepts such names since the NUL check in _zip_dirent_read only runs when consistency is checked:

  • zip_name_locate found an entry named a\0x when asked for a. The caller received data belonging to a different file than the name it asked for, which is a name confusion vector for applications that check for or extract specific entries by name from untrusted archives.
  • Adding a file literally named a to an archive containing a\0x failed with ZIP_ER_EXISTS, since the duplicate check matched the truncated prefix.

The same truncation existed in the fallback scan used with ZIP_FL_NOCASE and ZIP_FL_NODIR.

Change

Names are now hashed and compared over their full raw length:

  • siphash takes an explicit length instead of using strlen,
  • hash entries store the name length and compare with memcmp,
  • _zip_hash_add, _zip_hash_delete and _zip_hash_lookup take a length argument, and all callers pass the length reported by the string layer (_zip_string_get) or the length of the C string for user supplied names,
  • the fallback scan in _zip_name_locate compares lengths and uses memcmp or strncasecmp, with the ZIP_FL_NODIR adjustment applied to the length as well.

Since Windows has no strncasecmp, it is detected and mapped to _strnicmp/strnicmp the same way strcasecmp already is.

Testing

The new regression test hash_nul_name opens an in memory archive holding two stored entries with the raw names a\0x and a\0y. On unfixed code it fails in the first check with found entry 0 for name "a", but no entry has that name and exits 1. With this change it passes: no entry is found for the name a (with and without ZIP_FL_NOCASE), a file named a can be added without a false duplicate error, and the archive then holds three entries. The full suite, 194 tests, passes, as do the upstream workflows (Linux, macOS, Windows, BSD, Solaris).

Copilot AI balanced review requested due to automatic review settings October 4, 2026 02:49

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@dillof dillof added the bug For issues, use type instead. label Oct 4, 2026
@dillof dillof added this to the later milestone Oct 4, 2026
@dillof

dillof commented Oct 4, 2026

Copy link
Copy Markdown
Member

@ManoharPaturi

Copy link
Copy Markdown
Contributor Author

Understood — since the lookup API takes NUL-terminated C strings, a caller cannot express a name containing NUL, so the case only arises for archives produced by other writers. Happy to close this if you would rather handle it differently upstream, or leave it parked as a reference for the investigation; the regression test documents the current behavior either way.

@dillof

dillof commented Oct 4, 2026

Copy link
Copy Markdown
Member

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

bug For issues, use type instead.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants