Skip Menu |
Report information
Id: 132415
Status: new
Priority: 0/
Queue: perl5

Owner: Nobody
Requestors: mauke- <l.mai [at] web.de>
Cc:
AdminCc:

Operating System: (no value)
PatchStatus: (no value)
Severity: low
Type: core
Perl Version: 5.26.0
Fixed In: (no value)



To: perlbug [...] perl.org
From: l.mai [...] web.de
Date: Wed, 08 Nov 2017 01:31:27 +0100
Subject: _MEM_WRAP_NEEDS_RUNTIME_CHECK (malloc wrapping) in handy.h broken
Download (untitled) / with headers
text/plain 5.7k
This is a bug report for perl from l.mai@web.de, generated with the help of perlbug 1.40 running under perl 5.26.0. ----------------------------------------------------------------- [Please describe your issue here] In handy.h there is this piece of code: # define _MEM_WRAP_NEEDS_RUNTIME_CHECK(n,t) \ (8 * sizeof(n) + sizeof(t) > sizeof(MEM_SIZE)) A comment claims: * It's mathematically equivalent to * max(n) * sizeof(t) > MEM_SIZE_MAX This is false because _MEM_WRAP_NEEDS_RUNTIME_CHECK currently always returns true (it's broken). It was lasted touched in commit 004073bac990d90244eb463f435c52d4040b36df "Simplify _MEM_WRAP_NEEDS_RUNTIME_CHECK()". The old version was: # define _MEM_WRAP_NEEDS_RUNTIME_CHECK(n,t) \ ( sizeof(MEM_SIZE) < sizeof(n) \ || sizeof(t) > ((MEM_SIZE)1 << 8*(sizeof(MEM_SIZE) - sizeof(n)))) I don't understand the new version, so I wrote a little C program to compare the two. Here's the output (compiled on a 32-bit platform): ( uchar, char ) max(1) * 1 > UINT4_MAX? | old:no | new:yes | expected:no (!) (ushort, char ) max(2) * 1 > UINT4_MAX? | old:no | new:yes | expected:no (!) ( uint, char ) max(4) * 1 > UINT4_MAX? | old:no | new:yes | expected:no (!) ( ulong, char ) max(4) * 1 > UINT4_MAX? | old:no | new:yes | expected:no (!) ( ulong, char ) max(4) * 1 > UINT4_MAX? | old:no | new:yes | expected:no (!) (ullong, char ) max(8) * 1 > UINT4_MAX? | old:yes | new:yes | expected:yes ( uchar, char [2] ) max(1) * 2 > UINT4_MAX? | old:no | new:yes | expected:no (!) (ushort, char [2] ) max(2) * 2 > UINT4_MAX? | old:no | new:yes | expected:no (!) ( uint, char [2] ) max(4) * 2 > UINT4_MAX? | old:yes | new:yes | expected:yes ( ulong, char [2] ) max(4) * 2 > UINT4_MAX? | old:yes | new:yes | expected:yes ( ulong, char [2] ) max(4) * 2 > UINT4_MAX? | old:yes | new:yes | expected:yes (ullong, char [2] ) max(8) * 2 > UINT4_MAX? | old:yes | new:yes | expected:yes ( uchar, char [0xffff] ) max(1) * 65535 > UINT4_MAX? | old:no | new:yes | expected:no (!) (ushort, char [0xffff] ) max(2) * 65535 > UINT4_MAX? | old:no | new:yes | expected:no (!) ( uint, char [0xffff] ) max(4) * 65535 > UINT4_MAX? | old:yes | new:yes | expected:yes ( ulong, char [0xffff] ) max(4) * 65535 > UINT4_MAX? | old:yes | new:yes | expected:yes ( ulong, char [0xffff] ) max(4) * 65535 > UINT4_MAX? | old:yes | new:yes | expected:yes (ullong, char [0xffff] ) max(8) * 65535 > UINT4_MAX? | old:yes | new:yes | expected:yes ( uchar, char [0xffff+1]) max(1) * 65536 > UINT4_MAX? | old:no | new:yes | expected:no (!) (ushort, char [0xffff+1]) max(2) * 65536 > UINT4_MAX? | old:no | new:yes | expected:no (!) ( uint, char [0xffff+1]) max(4) * 65536 > UINT4_MAX? | old:yes | new:yes | expected:yes ( ulong, char [0xffff+1]) max(4) * 65536 > UINT4_MAX? | old:yes | new:yes | expected:yes ( ulong, char [0xffff+1]) max(4) * 65536 > UINT4_MAX? | old:yes | new:yes | expected:yes (ullong, char [0xffff+1]) max(8) * 65536 > UINT4_MAX? | old:yes | new:yes | expected:yes [Please do not change anything below this line] ----------------------------------------------------------------- --- Flags: category=core severity=low --- Site configuration information for perl 5.26.0: Configured by mauke at Fri Sep 22 13:28:36 CEST 2017. Summary of my perl5 (revision 5 version 26 subversion 0) configuration: Platform: osname=linux osvers=4.9.41-1-lts archname=i686-linux uname='linux simplicio 4.9.41-1-lts #1 smp mon aug 7 17:57:02 cest 2017 i686 gnulinux ' config_args='' hint=recommended useposix=true d_sigaction=define useithreads=undef usemultiplicity=undef use64bitint=undef use64bitall=undef uselongdouble=undef usemymalloc=n default_inc_excludes_dot=define bincompat5005=undef Compiler: cc='cc' ccflags ='-fwrapv -fno-strict-aliasing -pipe -fstack-protector-strong -I/usr/local/include -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64' optimize='-O2 -march=native' cppflags='-fwrapv -fno-strict-aliasing -pipe -fstack-protector-strong -I/usr/local/include' ccversion='' gccversion='7.2.0' gccosandvers='' intsize=4 longsize=4 ptrsize=4 doublesize=8 byteorder=1234 doublekind=3 d_longlong=define longlongsize=8 d_longdbl=define longdblsize=12 longdblkind=3 ivtype='long' ivsize=4 nvtype='double' nvsize=8 Off_t='off_t' lseeksize=8 alignbytes=4 prototype=define Linker and Libraries: ld='cc' ldflags ='-fstack-protector-strong -L/usr/local/lib' libpth=/usr/local/lib /usr/lib/gcc/i686-pc-linux-gnu/7.2.0/include-fixed /usr/lib /lib libs=-lpthread -lnsl -lgdbm -ldb -ldl -lm -lcrypt -lutil -lc -lgdbm_compat perllibs=-lpthread -lnsl -ldl -lm -lcrypt -lutil -lc libc=libc-2.26.so so=so useshrplib=false libperl=libperl.a gnulibc_version='2.26' Dynamic Linking: dlsrc=dl_dlopen.xs dlext=so d_dlsymun=undef ccdlflags='-Wl,-E' cccdlflags='-fPIC' lddlflags='-shared -O2 -march=native -L/usr/local/lib -fstack-protector-strong' --- @INC for perl 5.26.0: /home/mauke/usr/lib/perl5/site_perl/5.26.0/i686-linux /home/mauke/usr/lib/perl5/site_perl/5.26.0 /home/mauke/usr/lib/perl5/5.26.0/i686-linux /home/mauke/usr/lib/perl5/5.26.0 --- Environment for perl 5.26.0: HOME=/home/mauke LANG=en_US.UTF-8 LANGUAGE=en_US LC_COLLATE=C LC_MONETARY=de_DE.UTF-8 LC_TIME=de_DE.UTF-8 LD_LIBRARY_PATH (unset) LOGDIR (unset) PATH=/home/mauke/perl5/perlbrew/bin:/home/mauke/bin:/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/bin/site_perl:/usr/bin/vendor_perl:/usr/bin/core_perl PERLBREW_BASHRC_VERSION=0.73 PERLBREW_HOME=/home/mauke/.perlbrew PERLBREW_ROOT=/home/mauke/perl5/perlbrew PERL_BADLANG (unset) PERL_UNICODE=SAL SHELL=/bin/bash
RT-Send-CC: perl5-porters [...] perl.org
Download (untitled) / with headers
text/plain 2.4k
On Tue, 07 Nov 2017 16:31:51 -0800, mauke- wrote: Show quoted text
> > I don't understand the new version, so I wrote a little C program to > compare > the two. Here's the output (compiled on a 32-bit platform): > > ( uchar, char ) max(1) * 1 > UINT4_MAX? | old:no | > new:yes | expected:no (!) > (ushort, char ) max(2) * 1 > UINT4_MAX? | old:no | > new:yes | expected:no (!) > ( uint, char ) max(4) * 1 > UINT4_MAX? | old:no | > new:yes | expected:no (!) > ( ulong, char ) max(4) * 1 > UINT4_MAX? | old:no | > new:yes | expected:no (!) > ( ulong, char ) max(4) * 1 > UINT4_MAX? | old:no | > new:yes | expected:no (!) > (ullong, char ) max(8) * 1 > UINT4_MAX? | old:yes | > new:yes | expected:yes > ( uchar, char [2] ) max(1) * 2 > UINT4_MAX? | old:no | > new:yes | expected:no (!) > (ushort, char [2] ) max(2) * 2 > UINT4_MAX? | old:no | > new:yes | expected:no (!) > ( uint, char [2] ) max(4) * 2 > UINT4_MAX? | old:yes | > new:yes | expected:yes > ( ulong, char [2] ) max(4) * 2 > UINT4_MAX? | old:yes | > new:yes | expected:yes > ( ulong, char [2] ) max(4) * 2 > UINT4_MAX? | old:yes | > new:yes | expected:yes > (ullong, char [2] ) max(8) * 2 > UINT4_MAX? | old:yes | > new:yes | expected:yes > ( uchar, char [0xffff] ) max(1) * 65535 > UINT4_MAX? | old:no | > new:yes | expected:no (!) > (ushort, char [0xffff] ) max(2) * 65535 > UINT4_MAX? | old:no | > new:yes | expected:no (!) > ( uint, char [0xffff] ) max(4) * 65535 > UINT4_MAX? | old:yes | > new:yes | expected:yes > ( ulong, char [0xffff] ) max(4) * 65535 > UINT4_MAX? | old:yes | > new:yes | expected:yes > ( ulong, char [0xffff] ) max(4) * 65535 > UINT4_MAX? | old:yes | > new:yes | expected:yes > (ullong, char [0xffff] ) max(8) * 65535 > UINT4_MAX? | old:yes | > new:yes | expected:yes > ( uchar, char [0xffff+1]) max(1) * 65536 > UINT4_MAX? | old:no | > new:yes | expected:no (!) > (ushort, char [0xffff+1]) max(2) * 65536 > UINT4_MAX? | old:no | > new:yes | expected:no (!) > ( uint, char [0xffff+1]) max(4) * 65536 > UINT4_MAX? | old:yes | > new:yes | expected:yes > ( ulong, char [0xffff+1]) max(4) * 65536 > UINT4_MAX? | old:yes | > new:yes | expected:yes > ( ulong, char [0xffff+1]) max(4) * 65536 > UINT4_MAX? | old:yes | > new:yes | expected:yes > (ullong, char [0xffff+1]) max(8) * 65536 > UINT4_MAX? | old:yes | > new:yes | expected:yes
Code attached.
Subject: try.c
Download try.c
text/x-csrc 1.6k
#include <stdio.h> #include <limits.h> #include <stdint.h> #ifndef MEM_SIZE #define MEM_SIZE size_t #endif #define MEM_SIZE_MAX ((MEM_SIZE)-1) #define MEM_WRAP_NEEDS_RUNTIME_CHECK_1(n, t) \ ( sizeof(MEM_SIZE) < sizeof(n) \ || sizeof(t) > ((MEM_SIZE)1 << 8*(sizeof(MEM_SIZE) - sizeof(n)))) #define MEM_WRAP_NEEDS_RUNTIME_CHECK_2(n, t) \ (8 * sizeof(n) + sizeof(t) > sizeof(MEM_SIZE)) #define ACTUALLY_OVERFLOWS(n, t) \ ((n) > MEM_SIZE_MAX / sizeof(t)) #define TRY_WITH2(n, t) \ printf("(%6s, %-15s) max(%zu) * %5zu > UINT%zu_MAX? | old:%-3s | new:%-3s | expected:%-3s %s\n", \ #n, #t, \ sizeof (n), sizeof (t), \ sizeof (MEM_SIZE), \ MEM_WRAP_NEEDS_RUNTIME_CHECK_1(n, t) ? "yes" : "no", \ MEM_WRAP_NEEDS_RUNTIME_CHECK_2(n, t) ? "yes" : "no", \ ACTUALLY_OVERFLOWS(n, t) ? "yes" : "no", \ MEM_WRAP_NEEDS_RUNTIME_CHECK_1(n, t) != ACTUALLY_OVERFLOWS(n, t) || \ MEM_WRAP_NEEDS_RUNTIME_CHECK_2(n, t) != ACTUALLY_OVERFLOWS(n, t) ? "(!)" : "") #define TRY_WITH(t) \ do { \ const unsigned char uchar = UCHAR_MAX; \ const unsigned short ushort = USHRT_MAX; \ const unsigned int uint = UINT_MAX; \ const unsigned long ulong = ULONG_MAX; \ const unsigned long long ullong = ULLONG_MAX; \ TRY_WITH2(uchar, t); \ TRY_WITH2(ushort, t); \ TRY_WITH2(uint, t); \ TRY_WITH2(ulong, t); \ TRY_WITH2(ulong, t); \ TRY_WITH2(ullong, t); \ } while (0) int main(void) { TRY_WITH(char); TRY_WITH(char [2]); TRY_WITH(char [0xffff]); TRY_WITH(char [0xffff+1]); }
RT-Send-CC: perl5-porters [...] perl.org
Download (untitled) / with headers
text/plain 1.2k
On Tue, 07 Nov 2017 16:31:51 -0800, mauke- wrote: Show quoted text
> > In handy.h there is this piece of code: > > # define _MEM_WRAP_NEEDS_RUNTIME_CHECK(n,t) \ > (8 * sizeof(n) + sizeof(t) > sizeof(MEM_SIZE)) > > A comment claims: > > * It's mathematically equivalent to > * max(n) * sizeof(t) > MEM_SIZE_MAX > > This is false because _MEM_WRAP_NEEDS_RUNTIME_CHECK currently always > returns > true (it's broken). > > It was lasted touched in commit > 004073bac990d90244eb463f435c52d4040b36df > "Simplify _MEM_WRAP_NEEDS_RUNTIME_CHECK()". > > The old version was: > > # define _MEM_WRAP_NEEDS_RUNTIME_CHECK(n,t) \ > ( sizeof(MEM_SIZE) < sizeof(n) \ > || sizeof(t) > ((MEM_SIZE)1 << 8*(sizeof(MEM_SIZE) - sizeof(n))))
I have now reverted this commit in 445198b9b50e1018da1c98005a88bdf15c964f23. I believe the new version could have been fixed by changing (8 * sizeof(n) + sizeof(t) > sizeof(MEM_SIZE)) to (8 * sizeof(n) + ILOG2(sizeof(t)) > 8 * sizeof(MEM_SIZE)) but then I'd have to define a macro to compute the integer log base 2 (rounded up) of sizeof(t) at compile time. This is possible but annoying (64 separate comparisons). It's also the opposite of a simplification. So unless we really need to do that, I'd rather not.


This service is sponsored and maintained by Best Practical Solutions and runs on Perl.org infrastructure.

For issues related to this RT instance (aka "perlbug"), please contact perlbug-admin at perl.org