-
Notifications
You must be signed in to change notification settings - Fork 307
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
Cryptlib: Track allocation sizes to fix realloc #543
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -19,20 +19,63 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. | |
// -- Memory-Allocation Routines -- | ||
// | ||
|
||
/* Prefix allocations with the allocation's total size. | ||
* | ||
* This is used to implement `realloc`, which needs to know the | ||
* allocation's current size so that the data can be copied over to the | ||
* new allocation. | ||
*/ | ||
struct AllocWithSize { | ||
/* Size of the whole allocation, including this `size` field. | ||
* | ||
* Allocations returned by AllocatePool are always eight-byte aligned | ||
* (according to the UEFI spec), so use an eight-byte `size` field to | ||
* make the `data` field also eight-byte aligned. */ | ||
UINT64 size; | ||
|
||
/* The beginning of the allocation returned to the caller of malloc. | ||
* | ||
* The pointer to this data is what will then get passed into realloc | ||
* and free; those functions use ptr_to_alloc_with_size to get the | ||
* pointer to the underlying pool allocation. */ | ||
UINT8 data[0]; | ||
}; | ||
|
||
/* Convert a `ptr` (as returned by malloc) to an AllocWithSize by | ||
* subtracting eight bytes. */ | ||
static struct AllocWithSize *ptr_to_alloc_with_size (void *ptr) { | ||
UINT8 *cptr = (UINT8*) ptr; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. superfluous cast from |
||
return (struct AllocWithSize*) (cptr - sizeof(struct AllocWithSize)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Inserting a space character in a cast expression after Furthermore, for consistency's sake with how function calls look (in edk2 and in this patch anyway), a space character should be inserted between the |
||
} | ||
|
||
/* Allocates memory blocks */ | ||
void *malloc (size_t size) | ||
{ | ||
return AllocatePool ((UINTN) size); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well this is a comment on old code, but anyway. If For example, we expect |
||
UINTN alloc_size = (UINTN) (size + sizeof(struct AllocWithSize)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same comment as above about the superfluous/misleading space after But much more importantly, you are introducing a potential integer overflow here. Assume the caller calculates the If the external / untrusted data source can convince the caller to pass in a large value such as Then you get a buffer overflow right after, when you assign Suggested check:
Yet another observation is that, for https://pubs.opengroup.org/onlinepubs/9699919799/functions/malloc.html and returning the |
||
|
||
struct AllocWithSize *alloc = AllocatePool (alloc_size); | ||
if (alloc) { | ||
alloc->size = alloc_size; | ||
return alloc->data; | ||
} else { | ||
return NULL; | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It looks like github lost a review comment of mine here. Here I pointed out that this should be formatted as follows -- no
|
||
} | ||
|
||
/* Reallocate memory blocks */ | ||
void *realloc (void *ptr, size_t size) | ||
{ | ||
// | ||
// BUG: hardcode OldSize == size! We have no any knowledge about | ||
// memory size of original pointer ptr. | ||
// | ||
return ReallocatePool (ptr, (UINTN) size, (UINTN) size); | ||
struct AllocWithSize *alloc = ptr_to_alloc_with_size (ptr); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is wrong; https://pubs.opengroup.org/onlinepubs/9699919799/functions/realloc.html |
||
UINTN old_size = alloc->size; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Therefore here you should assert that For good measure, also assert that |
||
UINTN new_size = (UINTN) (size + sizeof(struct AllocWithSize)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well, more of the same overflow and style issues as pointed out for |
||
|
||
alloc = ReallocatePool (alloc, old_size, new_size); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Two things to consider here. (1) At this point, we're past the https://pubs.opengroup.org/onlinepubs/9699919799/functions/realloc.html and returning the (2) Unfortunately, the other thing to consider is a nasty preexistent bug, inside ReallocatePool(). As of commit 657b248, shim consumes its own fork of gnu-efi (the submodule) at commit 03670e14f263. And at that point, the
You do realize that this So, from our
That means this ReallocatePool() execution, internal to our realloc() implementation, is an actual resizing operation. And if the new area cannot be allocated, we're going to trash the existent area nevertheless. We're going to propagate In other words, ... It's so very demoralizing that no matter where I look in shim, I run into errors. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for your detailed review comments. I ended up closing this PR as discussed below. The new PR does not call
Well, we just have to fix things one step at a time :) I know it can be frustrating to find errors, but taking a step back I don't think it's fair to stay there's no healthy code to build fixes on. Just because there are additional errors that need to be fixed doesn't mean it's not worth it to go ahead and fix some bugs. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for the comment. In general I agree with that attitude. It's just that having to fix (say) three bugs as a dependency chain for the one bug you actually want to fix, is frustrating enough in itself. On top, not seeing any maintainer feedback for a good while in the discussion phase, does not evoke a lot of trust that your work, which has just grown unexpectedly in scope, will not be in vain. "One step at a time" is definitely the way to go, but once it grows into "one series at a time", maintainer overload becomes an even worse obstacle, and a strong discouragement. It makes me doubt my effort is worthwhile. But yes, in general you are right. |
||
if (alloc) { | ||
alloc->size = new_size; | ||
return alloc->data; | ||
} else { | ||
return NULL; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same else-after-return issue as with malloc |
||
} | ||
} | ||
|
||
/* De-allocates or frees a memory block */ | ||
|
@@ -43,6 +86,8 @@ void free (void *ptr) | |
// is not true of FreePool() below, so protect it. | ||
// | ||
if (ptr != NULL) { | ||
ptr = ptr_to_alloc_with_size (ptr); | ||
|
||
FreePool (ptr); | ||
} | ||
} |
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.
AFAICT the shim makefiles pass
-std=gnu11
, so here you should likely use the flexible array member, which was introduced in C99:data[]
, rather than the zero-length array GCC extension.