summaryrefslogtreecommitdiff
path: root/malloc.c
diff options
context:
space:
mode:
authorKarl Williamson <khw@cpan.org>2019-02-16 11:44:56 -0700
committerKarl Williamson <khw@cpan.org>2019-02-16 12:07:54 -0700
commitf966a6e60c5062904734d261eea96ba070f47fdb (patch)
tree6f264621e58abe165546dec79be66f2330c9f4ad /malloc.c
parenta4eb0505c203b947fe64e98ace1af076f4c12153 (diff)
downloadperl-f966a6e60c5062904734d261eea96ba070f47fdb.tar.gz
malloc.c: Limit malloc size to PTRDIFF_MAX
Without doing this, it is possible that the behavior is undefined when subtracting two pointers that point to the same object. See thread beginning at http://nntp.perl.org/group/perl.perl5.porters/251541 In particular this from Tomasz Konojacki C11 says: > When two pointers are subtracted, both shall point to elements of the > same array object, or one past the last element of the array object; > the result is the difference of the subscripts of the two array > elements. The size of the result is implementation-defined, and its > type (a signed integer type) is ptrdiff_t defined in the <stddef.h> > header. If the result is not representable in an object of that type, > the behavior is undefined. There are many ways to interpret this passage, but according to (most?) C compilers developers, it means that no object can be larger than PTRDIFF_MAX. For example, gcc's optimizer assummes that strlen() will never return anything larger than PTRDIFF_MAX [1]. There's also a blogpost[2] on this topic, which IMO is a very interesting read. If gcc and clang can assume that all objects won't be larger than PTRDIFF_MAX, so can we. Also, in practice, ssize_t and ptrdiff_t on most (all?) platforms are defined as exactly the same type. BTW, the fact that compilers assume that objects can't be larger than PTRDIFF_MAX has very dangerous implications on 32-bit platforms. Is it possible to create string longer than PTRDIFF_MAX on 32-bit perls?. It shouldn't be allowed. [1] - https://gcc.gnu.org/bugzilla/show_bug.cgi?id=78153 [2] - https://trust-in-soft.com/objects-larger-than-ptrdiff_max-bytes/
Diffstat (limited to 'malloc.c')
-rw-r--r--malloc.c12
1 files changed, 12 insertions, 0 deletions
diff --git a/malloc.c b/malloc.c
index 53835e1f47..72cf2cd307 100644
--- a/malloc.c
+++ b/malloc.c
@@ -1230,6 +1230,18 @@ Perl_malloc(size_t nbytes)
union overhead *p;
int bucket;
+ /* A structure that has more than PTRDIFF_MAX bytes is unfortunately
+ * legal in C, but in such, if two elements are far enough apart, we
+ * can't legally find out how far apart they are. Limit the size of a
+ * malloc so that pointer subtraction in the same structure is always
+ * well defined */
+ if (nbytes > PTRDIFF_MAX) {
+ MYMALLOC_WRITE2STDERR("Memory requests are limited to PTRDIFF_MAX"
+ " bytes to prevent possible undefined"
+ " behavior");
+ return NULL;
+ }
+
#if defined(DEBUGGING) || defined(RCHECK)
MEM_SIZE size = nbytes;
#endif