On 02/02/2018 10:35 AM, Bruno Haible wrote:
Done:
Thanks. Some comments, with a proposed patch attached:
# define malloca(N) \
- ((N) < 4032 - sa_increment \
- ? (void *) ((char *) alloca ((N) + sa_increment) + sa_increment) \
+ ((N) < 4032 - (2 * sa_alignment_max - 1) \
+ ? (void *) (((uintptr_t) alloca ((N) + 2 * sa_alignment_max - 1) \
+ + (2 * sa_alignment_max - 1)) \
+ & ~(uintptr_t)(2 * sa_alignment_max - 1)) \
: mmalloca (N))
This can cause problems when -fcheck-pointer-bounds is in effect, since
converting a pointer to uintptr_t and back means that GCC won't connect
the resulting pointer to the original and this messes up bounds checking
on the result.
! /* Type for holding very small pointer differences. */
! typedef unsigned char small_t;
There should be a compile-time check guaranteeing that small_t is wide
enough.
! size_t nplus = n + sizeof (small_t) + 2 * sa_alignment_max - 1;
For expressions like these, it's a bit better to parenthesize the value
added to N, mostly because it makes it clearer to the reader that we're
just adding a constant. Also, on (admittedly-weird) platforms where
SIZE_MAX <= INT_MAX, it avoids undefined behavior in some
(admittedly-unusual) cases.
! void
freea (void *p)
{
! /* Determine whether p was a non-NULL pointer returned by mmalloca(). */
! if ((uintptr_t) p & sa_alignment_max)
This should be "((uintptr_t) p & (2 * sa_alignment_max - 1))", to make
it more likely that a runtime error is detected if a garbage pointer is
passed to freea.
From 381dccd214bae32992664a5bab432d166bdecd00 Mon Sep 17 00:00:00 2001
From: Paul Eggert <egg...@cs.ucla.edu>
Date: Fri, 2 Feb 2018 14:07:19 -0800
Subject: [PATCH] malloca, xmalloca: port to -fcheck-pointer-bounds
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* lib/malloca.c (small_t): Verify that it is wide enough.
(mmalloca) [HAVE_ALLOCA]: Be a bit safer about size arithmetic.
Use malloca_alignoff to avoid issues with -fcheck-pointer-bounds.
(freea): Always use ‘free’ when the pointer’s low-order bits
(sa_alignment_max - 1) are nonzero. This is cheap and can catch
bad pointers earlier.
* lib/malloca.h (malloca_alignoff): New macro.
(malloca) [HAVE_ALLOCA]: Use it.
* lib/xmalloca.h (xmalloca) [HAVE_ALLOCA]: Use it.
* modules/malloca (Depends-on): Add verify.
---
ChangeLog | 14 ++++++++++++++
lib/malloca.c | 21 +++++++++++++--------
lib/malloca.h | 25 ++++++++++++++++++++++---
lib/xmalloca.h | 6 +++---
modules/malloca | 1 +
5 files changed, 53 insertions(+), 14 deletions(-)
diff --git a/ChangeLog b/ChangeLog
index 898d27592..30eec2148 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,17 @@
+2018-02-02 Paul Eggert <egg...@cs.ucla.edu>
+
+ malloca, xmalloca: port to -fcheck-pointer-bounds
+ * lib/malloca.c (small_t): Verify that it is wide enough.
+ (mmalloca) [HAVE_ALLOCA]: Be a bit safer about size arithmetic.
+ Use malloca_alignoff to avoid issues with -fcheck-pointer-bounds.
+ (freea): Always use ‘free’ when the pointer’s low-order bits
+ (sa_alignment_max - 1) are nonzero. This is cheap and can catch
+ bad pointers earlier.
+ * lib/malloca.h (malloca_alignoff): New macro.
+ (malloca) [HAVE_ALLOCA]: Use it.
+ * lib/xmalloca.h (xmalloca) [HAVE_ALLOCA]: Use it.
+ * modules/malloca (Depends-on): Add verify.
+
2018-02-02 Bruno Haible <br...@clisp.org>
malloca, xmalloca: Make multithread-safe.
diff --git a/lib/malloca.c b/lib/malloca.c
index 5741cba6f..f764add4d 100644
--- a/lib/malloca.c
+++ b/lib/malloca.c
@@ -21,6 +21,8 @@
/* Specification. */
#include "malloca.h"
+#include "verify.h"
+
/* The speed critical point in this file is freea() applied to an alloca()
result: it must be fast, to match the speed of alloca(). The speed of
mmalloca() and freea() in the other case are not critical, because they
@@ -34,14 +36,15 @@
/* Type for holding very small pointer differences. */
typedef unsigned char small_t;
+verify (2 * sa_alignment_max - 1 <= (small_t) -1);
void *
mmalloca (size_t n)
{
#if HAVE_ALLOCA
- /* Allocate one more word, used to determine the address to pass to freea(),
+ /* Allocate one more byte, used to determine the address to pass to freea(),
and room for the alignment ≡ sa_alignment_max mod 2*sa_alignment_max. */
- size_t nplus = n + sizeof (small_t) + 2 * sa_alignment_max - 1;
+ size_t nplus = n + (sizeof (small_t) + 2 * sa_alignment_max - 1);
if (nplus >= n)
{
@@ -49,10 +52,9 @@ mmalloca (size_t n)
if (mem != NULL)
{
- char *p =
- (char *)((((uintptr_t)mem + sizeof (small_t) + sa_alignment_max -
1)
- & ~(uintptr_t)(2 * sa_alignment_max - 1))
- + sa_alignment_max);
+ char *p = malloca_alignoff (mem + (sizeof (small_t)
+ + sa_alignment_max - 1),
+ 2 * sa_alignment_max, sa_alignment_max);
/* Here p >= mem + sizeof (small_t),
and p <= mem + sizeof (small_t) + 2 * sa_alignment_max - 1
hence p + n <= mem + nplus.
@@ -78,8 +80,11 @@ mmalloca (size_t n)
void
freea (void *p)
{
- /* Determine whether p was a non-NULL pointer returned by mmalloca(). */
- if ((uintptr_t) p & sa_alignment_max)
+ /* Determine whether P was a non-NULL pointer returned by mmalloca().
+ Although the mask sa_alignment_max would work just as well with
+ correct code, the mask (2 * sa_alignment_max - 1) can improve
+ error detection if P is a garbage pointer. */
+ if ((uintptr_t) p & (2 * sa_alignment_max - 1))
{
void *mem = (char *) p - ((small_t *) p)[-1];
free (mem);
diff --git a/lib/malloca.h b/lib/malloca.h
index cbc8fe7ab..c45705974 100644
--- a/lib/malloca.h
+++ b/lib/malloca.h
@@ -51,15 +51,34 @@ extern "C" {
# define safe_alloca(N) ((void) (N), NULL)
#endif
+/* Return the pointer P, first aligned down to the previous multiple
+ of the alignment A, and then with offset O added. A should be a
+ power of 2; O should be nonnegative and less than A. Ordinarily do
+ this simply by ANDing out unwanted bits from the uintptr_t
+ representation before adding O. But if __CHKP__ is defined, do not
+ convert back from uintptr_t, so that the compiler tracks that the
+ resulting pointer is derived from the original. Note that all
+ known __CHKP__ compilers support expression statements. */
+#ifdef __CHKP__
+# define malloca_alignoff(p, a, o) \
+ ({ char *malloca_p = (char *) (p); \
+ ptrdiff_t malloca_o = o; \
+ ptrdiff_t malloca_x = (uintptr_t) malloca_p & ((a) - 1); \
+ (void *) (malloca_p + (malloca_o - malloca_x)); })
+#else
+# define malloca_alignoff(p, a, o) \
+ ((void *) ((char *) (((uintptr_t) (p) + ((a) - 1)) & ~ ((a) - 1)) + (o)))
+# endif
+
/* malloca(N) is a safe variant of alloca(N). It allocates N bytes of
memory allocated on the stack, that must be freed using freea() before
the function returns. Upon failure, it returns NULL. */
#if HAVE_ALLOCA
# define malloca(N) \
((N) < 4032 - (2 * sa_alignment_max - 1) \
- ? (void *) (((uintptr_t) alloca ((N) + 2 * sa_alignment_max - 1) \
- + (2 * sa_alignment_max - 1)) \
- & ~(uintptr_t)(2 * sa_alignment_max - 1)) \
+ ? malloca_alignoff (((char *) alloca ((N) + (2 * sa_alignment_max - 1)) \
+ + (2 * sa_alignment_max - 1)), \
+ 2 * sa_alignment_max, 0) \
: mmalloca (N))
#else
# define malloca(N) \
diff --git a/lib/xmalloca.h b/lib/xmalloca.h
index 14fc1b9dc..a2d110db6 100644
--- a/lib/xmalloca.h
+++ b/lib/xmalloca.h
@@ -33,9 +33,9 @@ extern "C" {
#if HAVE_ALLOCA
# define xmalloca(N) \
((N) < 4032 - (2 * sa_alignment_max - 1) \
- ? (void *) (((uintptr_t) alloca ((N) + 2 * sa_alignment_max - 1) \
- + (2 * sa_alignment_max - 1)) \
- & ~(uintptr_t)(2 * sa_alignment_max - 1)) \
+ ? malloca_alignoff (((char *) alloca ((N) + (2 * sa_alignment_max - 1)) \
+ + (2 * sa_alignment_max - 1)), \
+ 2 * sa_alignment_max, 0) \
: xmmalloca (N))
extern void * xmmalloca (size_t n);
#else
diff --git a/modules/malloca b/modules/malloca
index 8f5ab644a..0ae3fe08d 100644
--- a/modules/malloca
+++ b/modules/malloca
@@ -11,6 +11,7 @@ m4/longlong.m4
Depends-on:
alloca-opt
stdint
+verify
xalloc-oversized
configure.ac:
--
2.14.3