1
Fork 0
mirror of git://git.sv.gnu.org/emacs.git synced 2025-12-11 00:30:17 -08:00

Avoid accessing uninitialized bool_vector words

Although loading uninitialized works from memory and then ignoring
the result works fine on conventional architectures, it
technically has undefined behavior in C, so redo bool_vector
allocation so that the code never does that.  This can improve
performance when allocating large vectors of nil, since calloc can
clear the memory lazily.
* src/alloc.c (make_clear_bool_vector): New function,
a generalization of make_uninit_bool_vector.
(make_uninit_bool_vector): Use it.
(Fmake_bool_vector): If !INIT, rely on make_clear_bool_vector.
* src/alloc.c (Fbool_vector):
* src/fns.c (Freverse): Don’t access uninitialized bool_vector words.
This commit is contained in:
Paul Eggert 2024-07-20 00:17:14 -07:00
parent cb78b43f39
commit 79b9f05d3a
3 changed files with 30 additions and 23 deletions

View file

@ -453,6 +453,7 @@ no_sanitize_memcpy (void *dest, void const *src, size_t size)
#endif /* MAX_SAVE_STACK > 0 */ #endif /* MAX_SAVE_STACK > 0 */
static struct Lisp_Vector *allocate_clear_vector (ptrdiff_t, bool);
static void unchain_finalizer (struct Lisp_Finalizer *); static void unchain_finalizer (struct Lisp_Finalizer *);
static void mark_terminals (void); static void mark_terminals (void);
static void gc_sweep (void); static void gc_sweep (void);
@ -2406,10 +2407,11 @@ bool_vector_fill (Lisp_Object a, Lisp_Object init)
return a; return a;
} }
/* Return a newly allocated, uninitialized bool vector of size NBITS. */ /* Return a newly allocated, bool vector of size NBITS. If CLEARIT,
clear its slots; otherwise the vector's slots are uninitialized. */
Lisp_Object Lisp_Object
make_uninit_bool_vector (EMACS_INT nbits) make_clear_bool_vector (EMACS_INT nbits, bool clearit)
{ {
Lisp_Object val; Lisp_Object val;
EMACS_INT words = bool_vector_words (nbits); EMACS_INT words = bool_vector_words (nbits);
@ -2420,28 +2422,35 @@ make_uninit_bool_vector (EMACS_INT nbits)
if (PTRDIFF_MAX < needed_elements) if (PTRDIFF_MAX < needed_elements)
memory_full (SIZE_MAX); memory_full (SIZE_MAX);
struct Lisp_Bool_Vector *p struct Lisp_Bool_Vector *p
= (struct Lisp_Bool_Vector *) allocate_vector (needed_elements); = (struct Lisp_Bool_Vector *) allocate_clear_vector (needed_elements,
clearit);
/* Clear padding at end; but only if necessary, to avoid polluting the
data cache. */
if (!clearit && nbits % BITS_PER_BITS_WORD != 0)
p->data[words - 1] = 0;
XSETVECTOR (val, p); XSETVECTOR (val, p);
XSETPVECTYPESIZE (XVECTOR (val), PVEC_BOOL_VECTOR, 0, 0); XSETPVECTYPESIZE (XVECTOR (val), PVEC_BOOL_VECTOR, 0, 0);
p->size = nbits; p->size = nbits;
/* Clear padding at the end. */
if (words)
p->data[words - 1] = 0;
return val; return val;
} }
/* Return a newly allocated, uninitialized bool vector of size NBITS. */
Lisp_Object
make_uninit_bool_vector (EMACS_INT nbits)
{
return make_clear_bool_vector (nbits, false);
}
DEFUN ("make-bool-vector", Fmake_bool_vector, Smake_bool_vector, 2, 2, 0, DEFUN ("make-bool-vector", Fmake_bool_vector, Smake_bool_vector, 2, 2, 0,
doc: /* Return a new bool-vector of length LENGTH, using INIT for each element. doc: /* Return a new bool-vector of length LENGTH, using INIT for each element.
LENGTH must be a number. INIT matters only in whether it is t or nil. */) LENGTH must be a number. INIT matters only in whether it is t or nil. */)
(Lisp_Object length, Lisp_Object init) (Lisp_Object length, Lisp_Object init)
{ {
Lisp_Object val;
CHECK_FIXNAT (length); CHECK_FIXNAT (length);
val = make_uninit_bool_vector (XFIXNAT (length)); Lisp_Object val = make_clear_bool_vector (XFIXNAT (length), NILP (init));
return bool_vector_fill (val, init); return NILP (init) ? val : bool_vector_fill (val, init);
} }
DEFUN ("bool-vector", Fbool_vector, Sbool_vector, 0, MANY, 0, DEFUN ("bool-vector", Fbool_vector, Sbool_vector, 0, MANY, 0,
@ -2450,13 +2459,10 @@ Allows any number of arguments, including zero.
usage: (bool-vector &rest OBJECTS) */) usage: (bool-vector &rest OBJECTS) */)
(ptrdiff_t nargs, Lisp_Object *args) (ptrdiff_t nargs, Lisp_Object *args)
{ {
ptrdiff_t i; Lisp_Object vector = make_clear_bool_vector (nargs, true);
Lisp_Object vector; for (ptrdiff_t i = 0; i < nargs; i++)
if (!NILP (args[i]))
vector = make_uninit_bool_vector (nargs); bool_vector_set (vector, i, true);
for (i = 0; i < nargs; i++)
bool_vector_set (vector, i, !NILP (args[i]));
return vector; return vector;
} }

View file

@ -2309,12 +2309,12 @@ See also the function `nreverse', which is used more often. */)
} }
else if (BOOL_VECTOR_P (seq)) else if (BOOL_VECTOR_P (seq))
{ {
ptrdiff_t i;
EMACS_INT nbits = bool_vector_size (seq); EMACS_INT nbits = bool_vector_size (seq);
new = make_uninit_bool_vector (nbits); new = make_clear_bool_vector (nbits, true);
for (i = 0; i < nbits; i++) for (ptrdiff_t i = 0; i < nbits; i++)
bool_vector_set (new, i, bool_vector_bitref (seq, nbits - i - 1)); if (bool_vector_bitref (seq, nbits - i - 1))
bool_vector_set (new, i, true);
} }
else if (STRINGP (seq)) else if (STRINGP (seq))
{ {

View file

@ -4578,6 +4578,7 @@ list4i (intmax_t a, intmax_t b, intmax_t c, intmax_t d)
return list4 (make_int (a), make_int (b), make_int (c), make_int (d)); return list4 (make_int (a), make_int (b), make_int (c), make_int (d));
} }
extern Lisp_Object make_clear_bool_vector (EMACS_INT, bool);
extern Lisp_Object make_uninit_bool_vector (EMACS_INT); extern Lisp_Object make_uninit_bool_vector (EMACS_INT);
extern Lisp_Object bool_vector_fill (Lisp_Object, Lisp_Object); extern Lisp_Object bool_vector_fill (Lisp_Object, Lisp_Object);
extern AVOID string_overflow (void); extern AVOID string_overflow (void);