Skip to content

Commit aab60f7

Browse files
authored
Merge pull request #361 from rust-osdev/gdt_check
gdt: Improve panic message when SystemSegment is pushed
2 parents 3a16af0 + c1d539a commit aab60f7

File tree

1 file changed

+72
-18
lines changed

1 file changed

+72
-18
lines changed

src/structures/gdt.rs

+72-18
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ use crate::registers::segmentation::{Segment, CS, SS};
4747
#[derive(Debug, Clone)]
4848
pub struct GlobalDescriptorTable {
4949
table: [u64; 8],
50-
next_free: usize,
50+
len: usize,
5151
}
5252

5353
impl GlobalDescriptorTable {
@@ -56,7 +56,7 @@ impl GlobalDescriptorTable {
5656
pub const fn new() -> GlobalDescriptorTable {
5757
GlobalDescriptorTable {
5858
table: [0; 8],
59-
next_free: 1,
59+
len: 1,
6060
}
6161
}
6262

@@ -68,40 +68,48 @@ impl GlobalDescriptorTable {
6868
/// * The provided slice **must not be larger than 8 items** (only up to the first 8 will be observed.)
6969
#[inline]
7070
pub const unsafe fn from_raw_slice(slice: &[u64]) -> GlobalDescriptorTable {
71-
let next_free = slice.len();
71+
let len = slice.len();
7272
let mut table = [0; 8];
7373
let mut idx = 0;
7474

7575
assert!(
76-
next_free <= 8,
76+
len <= 8,
7777
"initializing a GDT from a slice requires it to be **at most** 8 elements."
7878
);
7979

80-
while idx != next_free {
80+
while idx < len {
8181
table[idx] = slice[idx];
8282
idx += 1;
8383
}
8484

85-
GlobalDescriptorTable { table, next_free }
85+
GlobalDescriptorTable { table, len }
8686
}
8787

8888
/// Get a reference to the internal table.
8989
///
9090
/// The resulting slice may contain system descriptors, which span two `u64`s.
9191
#[inline]
9292
pub fn as_raw_slice(&self) -> &[u64] {
93-
&self.table[..self.next_free]
93+
&self.table[..self.len]
9494
}
9595

9696
/// Adds the given segment descriptor to the GDT, returning the segment selector.
9797
///
98-
/// Panics if the GDT has no free entries left.
98+
/// Panics if the GDT doesn't have enough free entries to hold the Descriptor.
9999
#[inline]
100100
#[cfg_attr(feature = "const_fn", rustversion::attr(all(), const))]
101101
pub fn add_entry(&mut self, entry: Descriptor) -> SegmentSelector {
102102
let index = match entry {
103-
Descriptor::UserSegment(value) => self.push(value),
103+
Descriptor::UserSegment(value) => {
104+
if self.len > self.table.len().saturating_sub(1) {
105+
panic!("GDT full")
106+
}
107+
self.push(value)
108+
}
104109
Descriptor::SystemSegment(value_low, value_high) => {
110+
if self.len > self.table.len().saturating_sub(2) {
111+
panic!("GDT requires two free spaces to hold a SystemSegment")
112+
}
105113
let index = self.push(value_low);
106114
self.push(value_high);
107115
index
@@ -157,14 +165,10 @@ impl GlobalDescriptorTable {
157165
#[inline]
158166
#[cfg_attr(feature = "const_fn", rustversion::attr(all(), const))]
159167
fn push(&mut self, value: u64) -> usize {
160-
if self.next_free < self.table.len() {
161-
let index = self.next_free;
162-
self.table[index] = value;
163-
self.next_free += 1;
164-
index
165-
} else {
166-
panic!("GDT full");
167-
}
168+
let index = self.len;
169+
self.table[index] = value;
170+
self.len += 1;
171+
index
168172
}
169173

170174
/// Creates the descriptor pointer for this table. This pointer can only be
@@ -174,7 +178,7 @@ impl GlobalDescriptorTable {
174178
use core::mem::size_of;
175179
super::DescriptorTablePointer {
176180
base: crate::VirtAddr::new(self.table.as_ptr() as u64),
177-
limit: (self.next_free * size_of::<u64>() - 1) as u16,
181+
limit: (self.len * size_of::<u64>() - 1) as u16,
178182
}
179183
}
180184
}
@@ -334,6 +338,7 @@ impl Descriptor {
334338
#[cfg(test)]
335339
mod tests {
336340
use super::DescriptorFlags as Flags;
341+
use super::*;
337342

338343
#[test]
339344
#[rustfmt::skip]
@@ -347,4 +352,53 @@ mod tests {
347352
assert_eq!(Flags::USER_CODE32.bits(), 0x00cffb000000ffff);
348353
assert_eq!(Flags::USER_DATA.bits(), 0x00cff3000000ffff);
349354
}
355+
356+
// Makes a GDT that has two free slots
357+
fn make_six_entry_gdt() -> GlobalDescriptorTable {
358+
let mut gdt = GlobalDescriptorTable::new();
359+
gdt.add_entry(Descriptor::kernel_code_segment());
360+
gdt.add_entry(Descriptor::kernel_data_segment());
361+
gdt.add_entry(Descriptor::UserSegment(DescriptorFlags::USER_CODE32.bits()));
362+
gdt.add_entry(Descriptor::user_data_segment());
363+
gdt.add_entry(Descriptor::user_code_segment());
364+
assert_eq!(gdt.len, 6);
365+
gdt
366+
}
367+
368+
static TSS: TaskStateSegment = TaskStateSegment::new();
369+
370+
fn make_full_gdt() -> GlobalDescriptorTable {
371+
let mut gdt = make_six_entry_gdt();
372+
gdt.add_entry(Descriptor::tss_segment(&TSS));
373+
assert_eq!(gdt.len, 8);
374+
gdt
375+
}
376+
377+
#[test]
378+
pub fn push_max_segments() {
379+
// Make sure we don't panic with user segments
380+
let mut gdt = make_six_entry_gdt();
381+
gdt.add_entry(Descriptor::user_data_segment());
382+
assert_eq!(gdt.len, 7);
383+
gdt.add_entry(Descriptor::user_data_segment());
384+
assert_eq!(gdt.len, 8);
385+
// Make sure we don't panic with system segments
386+
let _ = make_full_gdt();
387+
}
388+
389+
#[test]
390+
#[should_panic]
391+
pub fn panic_user_segment() {
392+
let mut gdt = make_full_gdt();
393+
gdt.add_entry(Descriptor::user_data_segment());
394+
}
395+
396+
#[test]
397+
#[should_panic]
398+
pub fn panic_system_segment() {
399+
let mut gdt = make_six_entry_gdt();
400+
gdt.add_entry(Descriptor::user_data_segment());
401+
// We have one free slot, but the GDT requires two
402+
gdt.add_entry(Descriptor::tss_segment(&TSS));
403+
}
350404
}

0 commit comments

Comments
 (0)