Skip to content

Fix module string copying and empty-needle scoring - #26

Open
lewang wants to merge 1 commit into
axelf4:masterfrom
lewang:master
Open

lewang wants to merge 1 commit into
axelf4:masterfrom
lewang:master

Conversation

@lewang

@lewang lewang commented Sep 7, 2026

Copy link
Copy Markdown

hotfuzz-module.c has three defects in copy_emacs_string and calc_cost. The
first makes the module unusable on Emacs 31; the other two are latent on every
version.

copy_emacs_string used an undersized buffer as a size query

The old code passed the remaining tail of the current bump block to
copy_string_contents to discover the string's length, treating a failure with
len != origlen as "too small, retry bigger".

That is not a supported size query. Emacs 31's module_copy_string_contents
signals memory-buffer-too-small when the supplied buffer is short, and the
retry path only cleared the non-local exit on one of the two failure branches —
so on a short block the error escaped into Lisp and filtering died with

Error: memory-buffer-too-small

Older versions don't signal, but they also don't reliably report the required
size when the shortfall is detected early, so the retry could allocate from a
stale len.

The documented query form is a NULL buffer, which is what this patch uses. The
copy then happens exactly once, into a block known to be large enough.

The bump reservation didn't cover strtolower's overwrite

strtolower rewrites the string in uint64_t-sized chunks, so it touches up to
alignof(uint64_t) - 1 bytes past the null-terminator. The allocation only
rounded up in the capacity < len branch; the opportunistic in-place path
advanced cursor past the aligned end without having checked that those bytes
were inside the block. A string landing near a block boundary could therefore be
written past limit.

This patch rounds the required size up front and uses that rounded value both to
test whether the string fits and to advance the cursor.

calc_cost indexed c[UINT_MAX] on an empty needle

calc_cost returns c[m - 1] with m unsigned. For an empty needle m is 0,
so it read c[UINT_MAX]. An empty needle matches everything at no cost, which
is now returned directly.

Notes for the maintainer

  • No behavioral change for non-empty needles: the cost matrix and bonus
    computation are untouched.
  • The rewritten copy_emacs_string drops the opportunistic
    "copy first, measure on failure" path in favor of one query plus one copy.
    That is one extra copy_string_contents call per string, and it removes the
    goto success / non_local_exit_clear control flow.

copy_emacs_string used a too-small buffer as a size query. Emacs 31 signals
memory-buffer-too-small in that case (module_copy_string_contents), so the
error escaped when the retry path did not clear it. Ask for the size with a
NULL buffer instead, which is the documented query form on all versions.

Also reserve the uint64_t-rounded size: strtolower rewrites the string in
8-byte chunks and touches up to 7 bytes past the null-terminator, which could
cross the end of a bump block.

calc_cost returned c[m - 1] with m unsigned, so an empty needle indexed
c[UINT_MAX] and crashed. An empty needle matches everything at no cost.
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.

1 participant