diff options
author | Karl Williamson <khw@cpan.org> | 2019-02-16 11:44:56 -0700 |
---|---|---|
committer | Karl Williamson <khw@cpan.org> | 2019-02-16 12:07:54 -0700 |
commit | f966a6e60c5062904734d261eea96ba070f47fdb (patch) | |
tree | 6f264621e58abe165546dec79be66f2330c9f4ad /malloc.c | |
parent | a4eb0505c203b947fe64e98ace1af076f4c12153 (diff) | |
download | perl-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.c | 12 |
1 files changed, 12 insertions, 0 deletions
@@ -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 |