Skip to content

pcre2_compile: zero-initialize code area before second-pass compilation - #929

Closed
KwisatzHaderach wants to merge 1 commit into
PCRE2Project:mainfrom
KwisatzHaderach:fix-msan-uninit-code-area
Closed

pcre2_compile: zero-initialize code area before second-pass compilation#929
KwisatzHaderach wants to merge 1 commit into
PCRE2Project:mainfrom
KwisatzHaderach:fix-msan-uninit-code-area

Conversation

@KwisatzHaderach

Copy link
Copy Markdown

When PCRE2 is used as a non-MSan-instrumented shared library inside an MSan-instrumented program, bytes written to the compiled code via direct assignment (e.g. literal character data for OP_CHAR opcodes) are not tracked in MSan's shadow memory. The JIT compiler's detect_repeat() calls memcmp() over compiled bytecode blocks, and MSan's memcmp interceptor reports a use-of-uninitialized-value error for those character bytes.

Fix by zeroing the code area with memset() before the second compilation pass. MSan's memset interceptor marks all code bytes as initialized in the shadow; the compiler then overwrites them with the actual bytecode values. This eliminates the spurious MSan error without any correctness impact.

Reproducer (requires clang + system PCRE2 without MSan instrumentation):

clang -fsanitize=memory -o test test.c -lpcre2-8
// test.c:
#define PCRE2_CODE_UNIT_WIDTH 8
#include <pcre2.h>
int main(void) {
int e; PCRE2_SIZE off;
pcre2_code *re = pcre2_compile(
(PCRE2_SPTR)"^(/foo/bar)(/foo/bar)$",
PCRE2_ZERO_TERMINATED, PCRE2_DOTALL, &e, &off, NULL);
pcre2_jit_compile(re, PCRE2_JIT_COMPLETE | PCRE2_JIT_PARTIAL_SOFT);
}
// => MSan: use-of-uninitialized-value in memcmp inside pcre2_jit_compile_8

Tested on: aarch64 (PCRE2 10.44) and x86_64 (PCRE2 10.47).

When PCRE2 is used as a non-MSan-instrumented shared library inside an
MSan-instrumented program, bytes written to the compiled code via direct
assignment (e.g. literal character data for OP_CHAR opcodes) are not tracked
in MSan's shadow memory. The JIT compiler's detect_repeat() calls memcmp()
over compiled bytecode blocks, and MSan's memcmp interceptor reports a
use-of-uninitialized-value error for those character bytes.

Fix by zeroing the code area with memset() before the second compilation pass.
MSan's memset interceptor marks all code bytes as initialized in the shadow;
the compiler then overwrites them with the actual bytecode values. This
eliminates the spurious MSan error without any correctness impact.

Reproducer (requires clang + system PCRE2 without MSan instrumentation):

  clang -fsanitize=memory -o test test.c -lpcre2-8
  // test.c:
  #define PCRE2_CODE_UNIT_WIDTH 8
  #include <pcre2.h>
  int main(void) {
    int e; PCRE2_SIZE off;
    pcre2_code *re = pcre2_compile(
      (PCRE2_SPTR)"^(/foo/bar)(/foo/bar)$",
      PCRE2_ZERO_TERMINATED, PCRE2_DOTALL, &e, &off, NULL);
    pcre2_jit_compile(re, PCRE2_JIT_COMPLETE | PCRE2_JIT_PARTIAL_SOFT);
  }
  // => MSan: use-of-uninitialized-value in memcmp inside pcre2_jit_compile_8

Tested on: aarch64 (PCRE2 10.44) and x86_64 (PCRE2 10.47).

Signed-off-by: Petr Matyas <p.matyas13@gmail.com>
@carenas

carenas commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

The documentation from memory sanitizer, mentions

MemorySanitizer requires that all program code is instrumented. This also includes any libraries that the program depends on, even libc. Failing to achieve this may result in false reports.

This doesn't reproduce if the PCRE2 library is instrumented as well, hence it is one of those false reports.

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.

2 participants