New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Review] All NN-argument functions potentially affected by GCC 4.8+ -O2-level mis-optimization #14795
Comments
From @ribasushiAs of version 4.8 GCC under -O2 compiles away all blocks of the form: if (! << argument marked with __attribute__((nonnull(1))) >> ) { ... } This does not happen under -O1. It is not clear which (if any) -fno* Currently the only *known* manifestation of this is perl 5.10.0 *However*, given that the current perl source is full of NN-marked I had a short chat with Nicholas, who also recommended I contact p5-sec: <Nicholas> I'd suggest mailing p5-sec, because that creates an RT ticket. Please let me know whether this is deemed high- or low-risk, as I would Cheers [1] http://rachid.koucha.free.fr/tech_corner/nonnull_gcc_attribute.html |
From @nwc10On Tue, Jul 07, 2015 at 08:57:44AM -0700, Peter Rabbitson wrote:
Sorry, I failed to ask "this is for blead" and assumed that it was. [I'm on holiday and don't have much time. Right now I have a little as my
I didn't remember when the assertions were added, but clearly it's post 5.10.0 Its code, as you linked to, is this: yy_parser * if (!proto) and yes, things like this are exactly the SEGV minefield you correctly yy_parser * PERL_ARGS_ASSERT_PARSER_DUP; where that macro is this: #define PERL_ARGS_ASSERT_PARSER_DUP \ (the macros I mentioned on IRC) and those assert()s in macros have picked up all places where the "not NULL" They were added (in bulk) by this commit: commit 7918f24 assert() that every NN argument is not NULL. Otherwise we have the 42 files changed, 4006 insertions(+), 85 deletions(-) which is merged to v5.10.1 and I'd infer that I only added it after I fixed everything that it found, commit cf684db In Perl_sv_catpv(), Perl_sv_catpv_mg() the ptr can be not NULL. (that includes the specific case that you hit) IIRC in maint-5.8 whilst most of the "not NULL" framework is there (to keep Hence, I think that 1) the only release at broad systematic risk is v5.10.0 3) we have the systematic risk mitigated So, unless (and until) we can find problems caused by "not NULL" on anything Nicholas Clark |
The RT System itself - Status changed from 'new' to 'open' |
From @jhiGiven all the pitfalls with the nonnull attribute (ranging from misleading and confusing to outright dangerous) could someone remind me of the benefits? Off-hand... Mostly it seems to just give permission for optimizers to do surprising things. Exploiting undefined behaviour by "this cannot happen so let's remove it". It certainly doesn't magically make code "NULL-resistant". If we only need to generating our own assertions, maybe we instead should use some attribute of our own?
|
From @iabynOn Thu, Jul 09, 2015 at 11:47:11AM +0300, Jarkko Hietaniemi wrote:
I think perhaps two things are getting conflated here by us and/or gcc: 1) telling the compiler that we *know* that function X will never be called 2) telling the run-time that this function *shouldn't* be called with a I think the PERL_ARGS_ASSERT_* are doing (2), while the The NN info we add to embed.fnc is for (2), so it's probably wrong -- |
From @LeontOn Thu, Jul 9, 2015 at 4:01 PM, Dave Mitchell <davem@iabyn.com> wrote:
Exactly.
Yeah, that may not be very helpful.
If I understand things correctly, those were defensive programming. We Leon |
From @ribasushiOn 07/08/2015 09:42 AM, Nicholas Clark via RT wrote:
If there is rough agreement on the above "nothing to see here" and/or Thanks! |
From @rjbs* Peter Rabbitson <rabbit@rabbit.us> [2015-07-15T07:08:49]
If there is no objection to this by Monday, I will do this... but on Monday I -- |
From @rjbsThis ticket has been moved to the perl5 queue. -- |
From [Unknown Contact. See original ticket]This ticket has been moved to the perl5 queue. -- |
From @bulk88I know the Win32 GCC build, with DEBUGGING on, is -O2, IDK why http://perl5.git.perl.org/perl.git/commitdiff/99efab1281ccea6f7df2a4d0affc5479291e2350 . So the assert for NULL will be optimized away. In hints/linux.sh I see case "$optimize" in So do linux GCC with DEBUGGING build at -O0 or -O2? Another question, since Perl runs on many platforms/CCs, is there a guarantee that referencing NULL reliable stops execution/SEGVs? What about dereferencing pointer -1? What about pointers 1 through 4096, incase pointer 0 succeeds through libc swallowing and fixing up the signals? What about a libc/CC/OS that allocates a whole valid page at addr 0 to addr page_size? I dont remember how big the interp struct is, 2 KB??? but it would be bad if addr 1.5 KB was readable, even though addr 0 SEGVs. http://h21007.www2.hp.com/portal/download/files/unprot/aCxx/Online_Help/options.htm#opt-capsz -Z Command-Line Option -Z This option allows dereferencing of null pointers at runtime. This is the default. The value of a dereferenced null pointer is zero. Perl uses -z flag to turn this off this "feature" on HP aCC. I've thought about adding some sanity tests to XS-APItest to make sure that things that are supposed to crash, CRASH! A nice mis-feature of the MS libc was, that there is a place where a try/catch [everything] block was silently swallowing SEGVs from heap corruption, and the libc function returns success (0) to perl interp. Many PP lines later, the perl interp crashes since the same malloc block/addr was being handed out over and over by malloc. I've attached a crude patch I wrote for myself to test that various things that should crash, crash. Perhaps it can be cleaned up by someone else and added for regular smoke testing. Some thought is required to make sure that a smoke box isn't going to quickly run out of disk space with core dumps while smoking. Illegal and privileged instruction are CPU specific. on 386, NULL bytes for instructions might not crash immediately (or ever) unless memory protection settings (ie, page not marked as code) causes a SEGV https://en.wikipedia.org/wiki/Data_Execution_Prevention . 0xFF/-1 is also valid on some CPUs. Only with PARISC and SPARC do 0xff and 0x00 both stop execution. Info below made with https://www.onlinedisassembler.com/odaweb/ 386 ARM PARISC MIPS POWERPC -- |
From @bulk880001-add-intentional-crashing-tests.patchFrom 01a65cb100c4e3e38c9b2c73183294ab92d2e67e Mon Sep 17 00:00:00 2001
From: Daniel Dragan <bulk88@hotmail.com>
Date: Fri, 23 Jan 2015 00:06:37 -0500
Subject: [PATCH] add intentional crashing tests
---
MANIFEST | 1 +
crashtest.pl | 12 +++++++
ext/XS-APItest/APItest.xs | 79 +++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 92 insertions(+), 0 deletions(-)
create mode 100644 crashtest.pl
diff --git a/MANIFEST b/MANIFEST
index 92a836d..8b805eb 100644
--- a/MANIFEST
+++ b/MANIFEST
@@ -2851,6 +2851,7 @@ cpan/Win32/t/Names.t See if Win32 extension works
cpan/Win32/t/Unicode.t See if Win32 extension works
cpan/Win32/Win32.pm Win32 extension Perl module
cpan/Win32/Win32.xs Win32 extension external subroutines
+crashtest.pl bulk88's testing of core crashing on win32
Cross/build-arm-n770-sh Cross-compilation
Cross/cflags-cross-arm Cross-compilation
Cross/config Cross-compilation
diff --git a/crashtest.pl b/crashtest.pl
new file mode 100644
index 0000000..98902d1
--- /dev/null
+++ b/crashtest.pl
@@ -0,0 +1,12 @@
+use Win32API::File;
+
+sub runtest {
+my $fn = shift;
+my $r = system(1, 'perl -Ilib -MXS::APItest -E"XS::APItest::'.$fn.'()"');
+my $p = wait();
+printf($fn.' $? %x CHILD_ERROR_NATIVE %x'."\n", $?, ${^CHILD_ERROR_NATIVE});
+}
+
+Win32API::File::SetErrorMode(Win32API::File::SEM_NOGPFAULTERRORBOX());
+runtest($_) foreach(qw(disable_interrupts illegal_instruction deref_null
+ deref_neg1 write_to_ro_mem div_by_0 call_c_debugger));
diff --git a/ext/XS-APItest/APItest.xs b/ext/XS-APItest/APItest.xs
index 075bb4d..8d79bfd 100644
--- a/ext/XS-APItest/APItest.xs
+++ b/ext/XS-APItest/APItest.xs
@@ -8,6 +8,28 @@
#include "XSUB.h"
#include "fakesdio.h" /* Causes us to use PerlIO below */
+#ifdef WIN32
+# include "dos.h"
+# ifdef _MSC_VER
+# pragma intrinsic(_disable)
+# else
+ /* even though _disable is declared as a func in dos.h, it isn't available */
+# define _disable() __asm__("cli")
+# endif
+# ifdef USING_MSVC6
+# pragma code_seg(".text")
+# else
+# pragma code_seg(push, ".text")
+# endif
+/* 0x0F 0x0B UD2 ins, 0xC3 retn ins, VC 64 doesnt support inline asm */
+__declspec(allocate(".text")) U8 ud2_ins[3] = { 0x0F, 0x0B, 0xC3 };
+# ifdef USING_MSVC6
+# pragma code_seg()
+# else
+# pragma code_seg(pop)
+# endif
+#endif
+
typedef SV *SVREF;
typedef PTR_TBL_t *XS__APItest__PtrTable;
@@ -3908,6 +3930,63 @@ test_newOP_CUSTOM()
OUTPUT:
RETVAL
+#ifdef WIN32
+void
+disable_interrupts()
+PPCODE:
+ /* disabling interrupts is illegal from user mode, causes EXCEPTION_PRIV_INSTRUCTION */
+ _disable();
+
+void
+illegal_instruction()
+PREINIT:
+ void (*fud2)() = (void (*)()) (void*)ud2_ins;
+PPCODE:
+ fud2();
+
+void
+call_c_debugger()
+PPCODE:
+ DebugBreak();
+
+#endif
+
+void
+deref_null()
+PREINIT:
+ int *nowhere = NULL;
+PPCODE:
+ *nowhere = 0;
+
+void
+deref_neg1()
+PREINIT:
+ int *nowhere = (int*)(SSize_t)-1;
+PPCODE:
+ *nowhere = 0;
+
+void
+write_to_ro_mem()
+PREINIT:
+ int *nowhere = (int*)PL_no_aelem;
+PPCODE:
+ *nowhere = 0;
+
+UV
+div_by_0(...)
+PREINIT:
+ UV divisor;
+CODE:
+ /* defeat CC optimizer */
+ if(items >= 1)
+ divisor = SvUV(ST(0));
+ else
+ divisor = 0;
+ RETVAL = 1 / divisor;
+OUTPUT:
+ RETVAL
+
+
MODULE = XS::APItest PACKAGE = XS::APItest::AUTOLOADtest
int
--
1.7.9.msysgit.0
|
From @tonycozOn Thu Jul 09 07:02:33 2015, davem wrote:
To me the correct solution is: 1) for -DDEBUGGING builds, don't use __attribute_nonnull__(), but have the asserts, 2) for non-DEBUGGING builds, use __attribute_nonnull__() for the optimization Currently we have 2), since we always have the attributes, and the asserts compile Do we want 1) ? Ideally we wouldn't: #define __attribute__nonnull(x) in debugging builds - it may interfere with system headers. Tony |
From @ap* Tony Cook via RT <perlbug-followup@perl.org> [2015-07-21 06:45]:
We NEVER want those optimization (dis)improvements. Even where we do, we GCC does not actually check the call sites of a function annotated with When given such a promise, it silently elides `if (!somepointer)` checks What’s the use of that? If we could truthfully make this promise to the In fact we should! Leaving them in the code but then obliquely eliding And of course this all means that we can only make use of this attribute This in turns means we also canNOT annotate ANY function that is exposed Of course if we elide the NULL checks from a function, we would want to The upshot is that __attribute_nonnull__ is worse than useless, at least Regards, |
From perl5-porters@perl.orgAristotle wrote:
I think you are missing the fact that, under debugging builds, we Or am I missing something? |
From @ap* Father Chrysostomos <sprout@cpan.org> [2015-07-21 18:10]:
What is this “optimisation” you speak of? That some checks get removed? I could maybe understand if __attribute_nonnull__ somehow made callers The *only* “optimisation” is some code gets omitted from the function. If you encounter if (!src) return FALSE; and this is a purely defensive check which is unnecessary in production #ifdef DEBUG so that it’s absolutely clear just from looking at the code that this OTOH, if that check *is* necessary in a production build, then why on Can you think of *any* scenario in which __attribute_nonnull__ is not Regards, |
From @LeontOn Tue, Jul 21, 2015 at 6:07 PM, Father Chrysostomos <sprout@cpan.org>
On production builds, that attribute should be a noop not an optimization. Leon |
From @jhis there any good reason we
To put it other way, why would one ever want in int foo(bar* p) the assert() to be *REMOVED* in a production build? (and only enabled in debug builds)? The removal would mean absolute confidence that nobody never calls foo() Or to spin it in yet another absurd way: "in production builds it is okay to dereference NULL". (There is whole another discussion about whether and when, if ever, code should abort() |
From @tonycozOn Tue, Jul 21, 2015 at 01:01:36PM +0200, Aristotle Pagaltzis wrote:
In some cases the if (!somepointer) test is embedded in a macro, and In a DEBUGGING build, all those checks should occur. Tony |
From @kentfredricOn 21 July 2015 at 15:21, bulk88 via RT <perlbug-followup@perl.org> wrote:
In case its relevant, this flag is happening at -O0 now, apparently:
In some of the articles Riba cited, some people observed -- KENTNL - https://metacpan.org/author/KENTNL |
Migrated from rt.perl.org#125570 (status was 'open')
Searchable as RT125570$
The text was updated successfully, but these errors were encountered: