-
Notifications
You must be signed in to change notification settings - Fork 161
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
Check list length in GrowPlist #2039
Check list length in GrowPlist #2039
Conversation
Codecov Report
@@ Coverage Diff @@
## stable-4.9 #2039 +/- ##
==============================================
- Coverage 66.01% 66.01% -0.01%
==============================================
Files 898 898
Lines 273370 273376 +6
Branches 12791 12792 +1
==============================================
+ Hits 180468 180470 +2
- Misses 90072 90077 +5
+ Partials 2830 2829 -1
|
src/gasman.h
Outdated
@@ -497,7 +497,6 @@ extern UInt ResizeBag ( | |||
Bag bag, | |||
UInt new_size ); | |||
|
|||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why? :-)
src/intobj.h
Outdated
*F INT_CAN_BE_INTOBJ( <i> ) . check if a C integer fits in an immediate | ||
** integer. | ||
*/ | ||
static inline Int INT_CAN_BE_INTOBJ(Int i) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I find this ambiguous -- the name could also be an alternative name for IS_INTOBJ
(i.e. "this integer could actually be an intobj, as opposed to being a bag ref"). In the description you use "fits", perhaps also use it in the macro name? So e.g. FITS_INTO_INTOBJ
.
src/intobj.h
Outdated
*/ | ||
static inline Int UINT_CAN_BE_INTOBJ(UInt i) | ||
{ | ||
return i < 1UL<<NR_SMALL_INT_BITS; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Off-topic remark: Strictly speaking this is not correct, as 1UL indicates an unsigned long
, but there are 64bit systems were long is still only 32bits (you need long long
for a 64bit type). Specifically, the LLP64 data model, which is what Windows uses on 64bit systems.
The same of course also holds for the 1L above. But of course we already use 1L and 1UL blissfully in lots of places, relying implicitly on all futures 64-systems GAP will run on to use LP64 (which is what Linux, *BSD, OSX use). Portable would be to use the INT64_C
and U INT64_C
etc. macros, resp. provide our own UINT_C
macro (which maps to either UINT64_C
or UINT32_C
depending on the host bit size.
But again, we already do this wrong in a ton of places, so this PR is not the place to start working on it, I just wanted to mention it because reading that line reminded me of it.
src/plist.c
Outdated
@@ -67,6 +67,12 @@ Int GrowPlist ( | |||
UInt plen; /* new physical length */ | |||
UInt good; /* good new physical length */ | |||
|
|||
if(!UINT_CAN_BE_INTOBJ(need)) | |||
{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Formatting...
bfdbbee
to
5cfbfb9
Compare
Hopefully all issues now fixed, formatted, random extra lines removed, (hopefully) better casting added. |
src/intobj.h
Outdated
*/ | ||
static inline Int INT_FITS_IN_INTOBJ(Int i) | ||
{ | ||
return (-((Int)1)<<NR_SMALL_INT_BITS) <= i) && |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Syntax error here, the final closing )
has no matching (
.
5cfbfb9
to
394727c
Compare
The code change looks fine to me, but it doesn't fix the problems mentioned in #1479 (Crashes from Todd-Coxeter). For example, in the branch of this PR after
So, the new code is triggered the first time, but not the second and third time. |
I think there is a deeper problem here, we don't need particularly big lists to cause a crash, for example, the following causes a segfault for me.
|
Also, when the crash happens, the list is a reasonable length. We just aren't garbage collecting all the lists, memory is being exhausted,.and for some reason a bad thing happens. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We definitely should get this merged
@ChrisJefferson you write: "This ensures GrowPlist checks the size we are growing the list to is a valid list size. This stops us then overflowing in ResizeBag later". But how exactly do we overflow there? |
I mean, the |
So, there are a few things going on here I think, although some might be misinterpreting memmove crashes. First of all, as the size of a plist must fit in an intermediate integer, there is no reason to ever grow a plist larger than that, and I think having a larger capacity confuses some other pieces of code. The resizebag problem is that once we get a huge list size, then multiply that by 4 (sizeof(Obj)), then we can overflow the check to see if there is enough free memory for a new.bag. As a side effect of keeping the size of a plist in a small int, the size of the resulting bag is at most 2^28*4 = 1GB, and all OSes preserve the top 1gb of memory space for kernel (I believe), so we don't overflow that check any more. Note I am away from computer and running on my memory here, so something here might not be quite right. It is certainly a good idea to not create stupidly large bags, bigger than any plist could require :) |
Note all these comments only apply to 32 bits. There is the same hypothetical problems in 64 bits, but no-one will ever come close to hitting them. |
Also it occured I believed the lengths of plists had to fit in an immediate integer, maybe that isn't true. But I would be willing to bet lots of code assumes it is true. |
I really want to understand more precisely what is going on before we merge this.
So there are several limitations at play here, with different things to do about them: If As to plists whose does not fit into an immediate integers causing a problem: Well, perhaps, but then, where exactly? Because as @frankluebeck pointed out, some care was already put into supporting that case (at least for strings, but also for other kinds of lists). I'd like to know about specific broken cases. I may just look for some myself, though, not asking you. Note that we already have two APIs to query the length of a list: Anyway, this use of Then, as @ChrisJefferson pointed out, a plist of length |
@@ -67,6 +67,11 @@ Int GrowPlist ( | |||
UInt plen; /* new physical length */ | |||
UInt good; /* good new physical length */ | |||
|
|||
if (!UINT_FITS_IN_INTOBJ(need)) { | |||
ErrorMayQuit("GrowPlist: List size too large", 0, 0); | |||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This check is actually not enough: A few lines below, we also "round" up the current capacity of the list, i.e.
good = 5 * CAPACITY_PLIST(list) / 4 + 4;
So if the list was close to the limit before, then good
will exceed the limit -- and then, even if need
is below the limit, we end up using good
(which is larger than need
) as the new list length.
I think the correct solution then is to first check here; then compute good
, and change
if ( need < good ) { plen = good; }
else { plen = need; }
to something like
if ( need < good && UINT_FITS_IN_INTOBJ(good) )
plen = good;
else
plen = need;
All of the intobj_int in listoper.c look like places where we assume plists (and in some cases lists I think :( ) indices fit in immediate integers. The overflow I thought I found was caused by a pointer overflow, if current upper limit + new bag size overflowed, but now I can't find it. Note i never thought the size of lists was required to fit in immediate integer, only plists. |
OK, the point with |
@ChrisJefferson I pushed a possible fixup to this PR. If you agree, please rebase it (and squash the fixup) -- I can't do that with your PR. |
Unfortunately I am literally on my phone until Jan 3rd or so. If you want to make a new PR, feel free! |
Closing this in favor of PR #2064 |
This ensures GrowPlist checks the size we are growing the list to is a valid list size. This stops us then overflowing in ResizeBag later.
I hope this fixes #1479, but I had trouble reproducing the error originally, so it is hard for me to check.
This also adds two explicit functions to check if a C signed or unsigned integer can be turned into an immediate integer. These functions can be used more widely, but I leave that for another PR (or at least until this one is checked to see if people like them).