From 0d10f10a6b2b26d831dc5b250197f83967f65719 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Thu, 9 Jun 2022 20:41:50 -0400 Subject: [PATCH v2] bindat (strz): Write null terminator after variable length string * lisp/emacs-lisp/bindat.el (bindat--pack-strz): Explicitly write a null byte after packing a variable-length string to ensure proper termination when packing to a pre-allocated string (bug#55952). * doc/lispref/processes.texi (Bindat Types): Update documentation. * test/lisp/emacs-lisp/bindat-tests.el (bindat-test--str-strz-prealloc): Update tests. --- doc/lispref/processes.texi | 25 ++++++++----------------- lisp/emacs-lisp/bindat.el | 3 +++ test/lisp/emacs-lisp/bindat-tests.el | 4 ++-- 3 files changed, 13 insertions(+), 19 deletions(-) diff --git a/doc/lispref/processes.texi b/doc/lispref/processes.texi index 8c8f8fd6b2..1018be3341 100644 --- a/doc/lispref/processes.texi +++ b/doc/lispref/processes.texi @@ -3495,23 +3495,14 @@ Bindat Types @item strz &optional @var{len} If @var{len} is not provided: Variable-length null-terminated unibyte string (@pxref{Text Representations}). When packing, the entire input -string is copied to the packed output. The following byte will be -null (zero) unless a pre-allocated string was provided to -@code{bindat-pack}, in which case that byte is left unmodified. The -length of the packed output is the length of the input string plus one -(for the null terminator). The input string must not contain any null -bytes. If the input string is multibyte with only ASCII and -@code{eight-bit} characters, it is converted to unibyte before it is -packed; other multibyte strings signal an error. When unpacking, the -resulting string contains all bytes up to (but excluding) the null -byte. - -@quotation Caution -If a pre-allocated string is provided to @code{bindat-pack}, the -packed output will not be properly null-terminated unless the -pre-allocated string already has a null byte at the appropriate -location. -@end quotation +string is copied to the packed output, and a null (zero) byte is +written after that. The length of the packed output is the length of +the input string plus one (for the null terminator). The input string +must not contain any null bytes. If the input string is multibyte +with only ASCII and @code{eight-bit} characters, it is converted to +unibyte before it is packed; other multibyte strings signal an error. +When unpacking, the resulting string contains all bytes up to (but +excluding) the null byte. If @var{len} is provided: @code{strz} behaves the same as @code{str} with one difference: When unpacking, the first null byte encountered diff --git a/lisp/emacs-lisp/bindat.el b/lisp/emacs-lisp/bindat.el index 9ba89a5e3f..46e2a4901c 100644 --- a/lisp/emacs-lisp/bindat.el +++ b/lisp/emacs-lisp/bindat.el @@ -450,6 +450,9 @@ bindat--pack-strz ;; need to scan the input string looking for a null byte. (error "Null byte encountered in input strz string")) (aset bindat-raw (+ bindat-idx i) (aref v i))) + ;; Explicitly write a null terminator in case the user provided a + ;; pre-allocated string to bindat-pack that wasn't zeroed first. + (aset bindat-raw (+ bindat-idx len) 0) (setq bindat-idx (+ bindat-idx len 1)))) (defun bindat--pack-bits (len v) diff --git a/test/lisp/emacs-lisp/bindat-tests.el b/test/lisp/emacs-lisp/bindat-tests.el index 7d1233ded7..cc223ad14e 100644 --- a/test/lisp/emacs-lisp/bindat-tests.el +++ b/test/lisp/emacs-lisp/bindat-tests.el @@ -182,8 +182,8 @@ bindat-test--str-strz-prealloc ((((x strz 2)) ((x . "a"))) . "ax") ((((x strz 2)) ((x . "ab"))) . "ab") ((((x strz 2)) ((x . "abc"))) . "ab") - ((,(bindat-type strz) "") . "xx") - ((,(bindat-type strz) "a") . "ax"))) + ((,(bindat-type strz) "") . "\0x") + ((,(bindat-type strz) "a") . "a\0"))) (let ((prealloc (make-string 2 ?x))) (apply #'bindat-pack (append (car tc) (list prealloc))) (should (equal prealloc (cdr tc)))))) -- 2.36.1