-
Notifications
You must be signed in to change notification settings - Fork 2
Conversation
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 think it'd be fine to defer tries
and shim handoff to a followup PR.
src/util/gpt.rs
Outdated
} | ||
|
||
block_io | ||
.read_blocks(self.my_lba, 1) |
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.
Leaked.
Also, why does this function read the block again rather than validating the data in the struct it was asked to validate?
src/util/gpt.rs
Outdated
(self.num_partition_entries * self.sizeof_partition_entry) as usize; | ||
|
||
block_io | ||
.read_bytes(self.partition_entry_lba, partition_entry_table_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.
Leaked.
src/util/gpt.rs
Outdated
pub struct GptDisk<'a> { | ||
block_device: &'static BlockIOProtocol, | ||
primary_header: &'a GptHeader, | ||
alternate_header: &'a GptHeader, |
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.
These should be freed on drop.
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.
They are; see lines 43-47. When a struct is dropped, its Drop
implementation is run, and then each of its children has their Drop
implementation run (see here for details). Since GptDisk
itself requires no special logic to drop, it doesn't need a Drop
implementation since the Drop
for GptHeader
will automatically be run for its children.
src/util/gpt.rs
Outdated
self.block_device | ||
.read_blocks(self.primary_header.alternate_lba, 1) | ||
.and_then(|block| { | ||
self.alternate_header = &*(block.as_ptr() as *const GptHeader); |
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 should not be happening in a function called validate
. If the alternate header needs to be loaded only after the primary has been validated, this function should be renamed or merged into read_from
.
src/util/gpt.rs
Outdated
.read_bytes( | ||
self.primary_header.partition_entry_lba, | ||
partition_entry_table_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.
Leaked.
Also, this is duplicated in the validation code. Perhaps read_from
should load all of the bits, validate
should check them without loading anything itself, and read_from
can generate the partition data structures as well. (validate
can be split into two parts for this if necessary.)
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 actually isn't leaked; read_partitions
returns a slice of GptPartitionEntry
references, not GptPartitionEntry
s, since GPT partition entries are variable length, and therefore cannot be stored directly in a slice that way.
src/picker.rs
Outdated
.and_then(parent_device_path) | ||
.and_then(|parent_path| { | ||
util::GptDisk::read_from(parent_path) | ||
.and_then(|disk| disk.read_partitions().map(util::gptprio::next)) |
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 the nesting here?
src/picker.rs
Outdated
let boot_result = menu::boot_menu(&option_a, &option_b) | ||
.and_then(|option| { | ||
match option { | ||
Some(user_choice) => Ok(user_choice), |
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.
Not necessarily a user_choice
, since it could have defaulted.
src/picker.rs
Outdated
Some(user_choice) => Ok(user_choice), | ||
None => { | ||
cons.write( | ||
"User chose nothing and no default was set. Can't proceed.\r\n", |
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.
Something like no option selected and
... would be a bit better.
On boot, determine the partition gptprio specifies, and use it as the default. As part of the boot menu, display the default choice to the user.
@bgilbert Updated. I think the new |
src/util/gpt.rs
Outdated
let alternate_header = unsafe { &mut *(alternate_block.as_mut_ptr() as *mut GptHeader) }; | ||
|
||
out.primary_header = primary_header; | ||
out.alternate_header = alternate_header; |
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.
Since you're not using out
before now, it seems that you can defer its creation until after the read_blocks
calls and avoid the null_mut
initializations.
src/util/gpt.rs
Outdated
self.alternate_header | ||
.validate(self.primary_header.alternate_lba)?; | ||
|
||
let partitions = self.read_partitions_raw()?; |
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.
Leaked.
It still seems as though these should be loaded once and stored, rather than loaded in validate
and then thrown away.
src/util/gpt.rs
Outdated
@@ -40,12 +40,6 @@ pub struct GptHeader { | |||
partition_entry_array_crc32: u32, | |||
} | |||
|
|||
impl Drop for GptHeader { | |||
fn drop(&mut self) { |
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 introduces a leak if let alternate_block = ...
fails.
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.
Hmm, there's no way for a GptHeader
to know how to free itself due to the allocate_pages
/allocate_pool
split. The best way to get around this is probably to introduce a type wrapping GptHeader
that also stores how to free itself.
src/util/gpt.rs
Outdated
let bs = uefi::get_system_table().boot_services(); | ||
|
||
let num_partitions = self.primary_header.num_partition_entries as usize; | ||
let partition_entry_table = self.read_partitions_raw()?; |
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.
Leaked. On error we can do something about that, and on success... it would be nice if we had a way to clean this up after the return value is dropped.
I believe the caller has an undocumented responsibility to free the returned array.
By splitting up the validation stage into validate_headers() and validate_partitions(), and splitting each of those into a reading and validation stage, leaks are avoided.
partitions: &[], | ||
}; | ||
|
||
unsafe { out.validate_headers()? }; |
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.
What happens if we fail here and Drop tries to free raw_partitions
?
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.
The implementation of free_pages
(and free_pool
) in rust-uefi ignores failure, so the actual UEFI call will fail, but that failure won't be passed along to picker, so from picker's perspective, everything will be fine.
src/util/gpt.rs
Outdated
bs.free_pool(self.alternate_header); | ||
} | ||
|
||
self.block_device.free_read(self.raw_partitions); |
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.
The partitions
array is not freed.
let primary_block = protocol.read_blocks(1, 1)?; | ||
let primary_header = unsafe { &mut *(primary_block.as_mut_ptr() as *mut GptHeader) }; | ||
|
||
let alternate_block = protocol.read_blocks(primary_header.alternate_lba, 1)?; |
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.
If you spell out the error check here you can avoid the primary_header
leak.
Since I have a few more things to change with this PR and there's apparently no way for me to update this one, I'll reopen a new PR once I have things figured out. |
This implementation of gptprio is read-only, in that it doesn't update the
tries
field of gptprio. It also does not pass anything to shim instructing it what to boot; if desired I can add that for this PR.Before this PR is merged, csssuf/rust-uefi#18 will need to be merged and a commit bumping
Cargo.lock
will need to be added to this PR.