Fix pcre2-release! double-free + keyword arg matching
ober
75cdc6698c853c7587e72f78fc17b9e6c56fcfba
--- a/src/jerboa-pcre2/pcre2.ss +++ b/src/jerboa-pcre2/pcre2.ss @@ -56,12 +56,12 @@ ;; ----------------------------------------------------------------------- (define-record-type pcre-regex - (fields code ; void* — pcre2_code pointer - match-data ; void* — pcre2_match_data pointer - pattern ; string — original pattern - capture-count ; integer — number of capturing groups - jit? ; boolean — JIT compilation succeeded - name-table) ; alist ((name . group-number) ...) + (fields (mutable code) ; void* — pcre2_code pointer (zeroed after release) + (mutable match-data) ; void* — pcre2_match_data pointer (zeroed after release) + pattern ; string — original pattern + capture-count ; integer — number of capturing groups + jit? ; boolean — JIT compilation succeeded + name-table) ; alist ((name . group-number) ...) (nongenerative pcre-regex)) (define-record-type pcre-match @@ -189,16 +189,22 @@ (values caseless multiline dotall extended ungreedy utf ucp literal jit)] [(and (pair? args) (pair? (cdr args))) (let ([key (car args)] [val (cadr args)]) - (case key - [(caseless:) (set! caseless val)] - [(multiline:) (set! multiline val)] - [(dotall:) (set! dotall val)] - [(extended:) (set! extended val)] - [(ungreedy:) (set! ungreedy val)] - [(utf:) (set! utf val)] - [(ucp:) (set! ucp val)] - [(literal:) (set! literal val)] - [(jit:) (set! jit val)] + ;; Use barred identifiers so the symbol "caseless:" is unambiguous + ;; under both readers. Jerboa's reader treats unescaped `name:` as + ;; keyword shorthand, which through case-clause transpilation + ;; degrades to a (list 'name:) literal that never matches. Chez's + ;; stock reader (used by --script tests) sees `'caseless:` as a + ;; plain symbol — `'|caseless:|` is the same plain symbol in both. + (cond + [(eq? key '|caseless:|) (set! caseless val)] + [(eq? key '|multiline:|) (set! multiline val)] + [(eq? key '|dotall:|) (set! dotall val)] + [(eq? key '|extended:|) (set! extended val)] + [(eq? key '|ungreedy:|) (set! ungreedy val)] + [(eq? key '|utf:|) (set! utf val)] + [(eq? key '|ucp:|) (set! ucp val)] + [(eq? key '|literal:|) (set! literal val)] + [(eq? key '|jit:|) (set! jit val)] [else (error 'pcre2-regex "unknown keyword" key)]) (loop (cddr args)))] [else (error 'pcre2-regex "invalid keyword arguments" args)])))) @@ -520,11 +526,18 @@ (when (pcre-regex? regex) ;; Remove from cache (set! *cache* (filter (lambda (e) (not (eq? (cdr e) regex))) *cache*)) - ;; Release FFI resources + ;; Release FFI resources, then ZERO the struct fields. The regex is + ;; still registered in pcre2-guardian; without zeroing, the eventual + ;; pcre2-collect! drain after GC would re-free the same C pointers, + ;; causing "invalid memory reference" on the next FFI call. (let ([md (pcre-regex-match-data regex)] [code (pcre-regex-code regex)]) - (when (not (zero? md)) (ffi-pcre2-match-data-free md)) - (when (not (zero? code)) (ffi-pcre2-code-free code))))) + (when (not (zero? md)) + (ffi-pcre2-match-data-free md) + (pcre-regex-match-data-set! regex 0)) + (when (not (zero? code)) + (ffi-pcre2-code-free code) + (pcre-regex-code-set! regex 0))))) ;; ----------------------------------------------------------------------- ;; Pregexp-compatible API