From 7e01e80a3f8ac4eed1e6d717994f9e07b8d5db18 Mon Sep 17 00:00:00 2001 From: Restioson Date: Sat, 17 Oct 2020 09:30:42 +0200 Subject: [PATCH 01/13] Add ability to add iomap to TSS --- src/structures/gdt.rs | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/structures/gdt.rs b/src/structures/gdt.rs index a071982f9..4ba926f80 100644 --- a/src/structures/gdt.rs +++ b/src/structures/gdt.rs @@ -306,6 +306,17 @@ impl Descriptor { /// Creates a TSS system descriptor for the given TSS. #[inline] pub fn tss_segment(tss: &'static TaskStateSegment) -> Descriptor { + // SAFETY: if iomap_size is zero, there are no requirements to uphold. + unsafe { Self::tss_segment_with_iomap(tss, 0) } + } + + /// Creates a TSS system descriptor for the given TSS, setting up the IO permissions bitmap. + /// + /// # Safety + /// + /// If `iomap_size` is greater than zero, there **must** be a valid IO map at `tss_ptr + iomap_base`. + /// The size of the IO map must correspond with the given `iomap_size`. + pub unsafe fn tss_segment_with_iomap(tss: &'static TaskStateSegment, iomap_size: u16) -> Descriptor { use self::DescriptorFlags as Flags; use core::mem::size_of; @@ -315,8 +326,11 @@ impl Descriptor { // base low.set_bits(16..40, ptr.get_bits(0..24)); low.set_bits(56..64, ptr.get_bits(24..32)); - // limit (the `-1` in needed since the bound is inclusive) - low.set_bits(0..16, (size_of::() - 1) as u64); + // limit (the `-1` is needed since the bound is inclusive) + low.set_bits( + 0..16, + (size_of::() + (tss.iomap_base + iomap_size) as usize - 1) as u64 + ); // type (0b1001 = available 64-bit tss) low.set_bits(40..44, 0b1001); From 290d0c67b38d8d0196fc4e25f26525b3403050a0 Mon Sep 17 00:00:00 2001 From: Restioson Date: Sat, 17 Oct 2020 09:37:26 +0200 Subject: [PATCH 02/13] formatting --- src/structures/gdt.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/structures/gdt.rs b/src/structures/gdt.rs index 4ba926f80..61cd293a7 100644 --- a/src/structures/gdt.rs +++ b/src/structures/gdt.rs @@ -316,7 +316,10 @@ impl Descriptor { /// /// If `iomap_size` is greater than zero, there **must** be a valid IO map at `tss_ptr + iomap_base`. /// The size of the IO map must correspond with the given `iomap_size`. - pub unsafe fn tss_segment_with_iomap(tss: &'static TaskStateSegment, iomap_size: u16) -> Descriptor { + pub unsafe fn tss_segment_with_iomap( + tss: &'static TaskStateSegment, + iomap_size: u16, + ) -> Descriptor { use self::DescriptorFlags as Flags; use core::mem::size_of; @@ -329,7 +332,7 @@ impl Descriptor { // limit (the `-1` is needed since the bound is inclusive) low.set_bits( 0..16, - (size_of::() + (tss.iomap_base + iomap_size) as usize - 1) as u64 + (size_of::() + (tss.iomap_base + iomap_size) as usize - 1) as u64, ); // type (0b1001 = available 64-bit tss) low.set_bits(40..44, 0b1001); From 7d2fa063d5f8539645e381fc0e78747e6d225094 Mon Sep 17 00:00:00 2001 From: Restioson Date: Sat, 17 Oct 2020 14:29:42 +0200 Subject: [PATCH 03/13] doc & iomap limit calculation --- src/structures/gdt.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/structures/gdt.rs b/src/structures/gdt.rs index 61cd293a7..48fc2e725 100644 --- a/src/structures/gdt.rs +++ b/src/structures/gdt.rs @@ -4,7 +4,7 @@ use crate::structures::{tss::TaskStateSegment, DescriptorTablePointer}; use crate::PrivilegeLevel; use bit_field::BitField; use bitflags::bitflags; -use core::fmt; +use core::{fmt, cmp, mem}; /// Specifies which element to load into a segment from /// descriptor tables (i.e., is a index to LDT or GDT table @@ -314,8 +314,8 @@ impl Descriptor { /// /// # Safety /// - /// If `iomap_size` is greater than zero, there **must** be a valid IO map at `tss_ptr + iomap_base`. - /// The size of the IO map must correspond with the given `iomap_size`. + /// There must be a valid IO map at `(tss as *const u8).offset(tss.iomap_base)` + /// of length `iomap_size` pub unsafe fn tss_segment_with_iomap( tss: &'static TaskStateSegment, iomap_size: u16, @@ -330,9 +330,10 @@ impl Descriptor { low.set_bits(16..40, ptr.get_bits(0..24)); low.set_bits(56..64, ptr.get_bits(24..32)); // limit (the `-1` is needed since the bound is inclusive) + let iomap_limit = tss.iomap_base as u64 + iomap_size as u64 - 1; low.set_bits( 0..16, - (size_of::() + (tss.iomap_base + iomap_size) as usize - 1) as u64, + cmp::max(mem::size_of::(), iomap_limit), ); // type (0b1001 = available 64-bit tss) low.set_bits(40..44, 0b1001); From 027ad7b34c1e121f82b05c36bc0004cf6229741b Mon Sep 17 00:00:00 2001 From: Restioson Date: Sat, 17 Oct 2020 14:33:21 +0200 Subject: [PATCH 04/13] missed a cast :( --- src/structures/gdt.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/structures/gdt.rs b/src/structures/gdt.rs index 48fc2e725..b885ec648 100644 --- a/src/structures/gdt.rs +++ b/src/structures/gdt.rs @@ -333,7 +333,7 @@ impl Descriptor { let iomap_limit = tss.iomap_base as u64 + iomap_size as u64 - 1; low.set_bits( 0..16, - cmp::max(mem::size_of::(), iomap_limit), + cmp::max(mem::size_of::() as u64, iomap_limit), ); // type (0b1001 = available 64-bit tss) low.set_bits(40..44, 0b1001); From ee27c96231fe4bf9d03a25795867ffa2fe029520 Mon Sep 17 00:00:00 2001 From: Restioson Date: Sat, 17 Oct 2020 14:41:02 +0200 Subject: [PATCH 05/13] expand on safety requirements --- src/structures/gdt.rs | 3 ++- src/structures/tss.rs | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/structures/gdt.rs b/src/structures/gdt.rs index b885ec648..4f4f3df6a 100644 --- a/src/structures/gdt.rs +++ b/src/structures/gdt.rs @@ -315,7 +315,8 @@ impl Descriptor { /// # Safety /// /// There must be a valid IO map at `(tss as *const u8).offset(tss.iomap_base)` - /// of length `iomap_size` + /// of length `iomap_size`, with the terminating `0xFF` byte. Additionally, `iomap_base` must + /// not exceed `0xDFFF`. pub unsafe fn tss_segment_with_iomap( tss: &'static TaskStateSegment, iomap_size: u16, diff --git a/src/structures/tss.rs b/src/structures/tss.rs index 882a3eebb..3231b3136 100644 --- a/src/structures/tss.rs +++ b/src/structures/tss.rs @@ -17,7 +17,8 @@ pub struct TaskStateSegment { pub interrupt_stack_table: [VirtAddr; 7], reserved_3: u64, reserved_4: u16, - /// The 16-bit offset to the I/O permission bit map from the 64-bit TSS base. + /// The 16-bit offset to the I/O permission bit map from the 64-bit TSS base. It must not + /// exceed `0xDFFF`. pub iomap_base: u16, } From c8d062d6b6fc38e98abd1cec77ec87b03281e71e Mon Sep 17 00:00:00 2001 From: Restioson Date: Sat, 17 Oct 2020 14:47:22 +0200 Subject: [PATCH 06/13] -1 after max --- src/structures/gdt.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/structures/gdt.rs b/src/structures/gdt.rs index 4f4f3df6a..d89cce1b1 100644 --- a/src/structures/gdt.rs +++ b/src/structures/gdt.rs @@ -322,7 +322,6 @@ impl Descriptor { iomap_size: u16, ) -> Descriptor { use self::DescriptorFlags as Flags; - use core::mem::size_of; let ptr = tss as *const _ as u64; @@ -331,10 +330,10 @@ impl Descriptor { low.set_bits(16..40, ptr.get_bits(0..24)); low.set_bits(56..64, ptr.get_bits(24..32)); // limit (the `-1` is needed since the bound is inclusive) - let iomap_limit = tss.iomap_base as u64 + iomap_size as u64 - 1; + let iomap_limit = tss.iomap_base as u64 + iomap_size as u64; low.set_bits( 0..16, - cmp::max(mem::size_of::() as u64, iomap_limit), + cmp::max(mem::size_of::() as u64, iomap_limit) - 1, ); // type (0b1001 = available 64-bit tss) low.set_bits(40..44, 0b1001); From e5a66124c3bc7f8b0bd83dc1259939b74bccfbe2 Mon Sep 17 00:00:00 2001 From: Restioson Date: Sat, 17 Oct 2020 14:47:56 +0200 Subject: [PATCH 07/13] fmt --- src/structures/gdt.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/structures/gdt.rs b/src/structures/gdt.rs index d89cce1b1..7ba6074b6 100644 --- a/src/structures/gdt.rs +++ b/src/structures/gdt.rs @@ -4,7 +4,7 @@ use crate::structures::{tss::TaskStateSegment, DescriptorTablePointer}; use crate::PrivilegeLevel; use bit_field::BitField; use bitflags::bitflags; -use core::{fmt, cmp, mem}; +use core::{cmp, fmt, mem}; /// Specifies which element to load into a segment from /// descriptor tables (i.e., is a index to LDT or GDT table From 7051f976fc3834dce1691aad28970c34b1a48487 Mon Sep 17 00:00:00 2001 From: Restioson Date: Sat, 17 Oct 2020 15:15:24 +0200 Subject: [PATCH 08/13] slice for safety --- src/structures/gdt.rs | 28 ++++++++++++++++++++++++++-- src/structures/tss.rs | 18 ++++++++++++++++++ 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/src/structures/gdt.rs b/src/structures/gdt.rs index 7ba6074b6..63a105e02 100644 --- a/src/structures/gdt.rs +++ b/src/structures/gdt.rs @@ -5,6 +5,7 @@ use crate::PrivilegeLevel; use bit_field::BitField; use bitflags::bitflags; use core::{cmp, fmt, mem}; +use crate::structures::tss::InvalidIoMap; /// Specifies which element to load into a segment from /// descriptor tables (i.e., is a index to LDT or GDT table @@ -307,7 +308,30 @@ impl Descriptor { #[inline] pub fn tss_segment(tss: &'static TaskStateSegment) -> Descriptor { // SAFETY: if iomap_size is zero, there are no requirements to uphold. - unsafe { Self::tss_segment_with_iomap(tss, 0) } + unsafe { Self::tss_segment_raw(tss, 0) } + } + + /// Creates a TSS system descriptor for the given TSS, setting up the IO permissions bitmap. + pub fn tss_segment_with_iomap( + tss: &'static TaskStateSegment, + iomap: &'static [u8], + ) -> Result { + if iomap.len() > 8193 { + return Err(InvalidIoMap::TooLong { len: iomap.len() }) + } + + let distance = iomap.as_ptr() as usize - tss as *const _ as usize; + if distance > 0xdfff { + return Err(InvalidIoMap::TooFarFromTss { distance }) + } + + let last_byte = *iomap.last().unwrap_or(&0xff); + if last_byte != 0xff { + return Err(InvalidIoMap::InvalidTerminatingByte { byte: last_byte }) + } + + // SAFETY: all invariants checked above + Ok(unsafe { Self::tss_segment_raw(tss, iomap.len() as u16) }) } /// Creates a TSS system descriptor for the given TSS, setting up the IO permissions bitmap. @@ -317,7 +341,7 @@ impl Descriptor { /// There must be a valid IO map at `(tss as *const u8).offset(tss.iomap_base)` /// of length `iomap_size`, with the terminating `0xFF` byte. Additionally, `iomap_base` must /// not exceed `0xDFFF`. - pub unsafe fn tss_segment_with_iomap( + unsafe fn tss_segment_raw( tss: &'static TaskStateSegment, iomap_size: u16, ) -> Descriptor { diff --git a/src/structures/tss.rs b/src/structures/tss.rs index 3231b3136..1d3178dd5 100644 --- a/src/structures/tss.rs +++ b/src/structures/tss.rs @@ -38,3 +38,21 @@ impl TaskStateSegment { } } } + +/// The given IO permissions bitmap is invalid. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum InvalidIoMap { + /// The IO permissions bitmap is too far from the TSS. It must be within `0xdfff` bytes of the + /// start of the TSS. + TooFarFromTss { + distance: usize, + }, + /// The final byte of the IO permissions bitmap was not 0xff + InvalidTerminatingByte { + byte: u8, + }, + /// The IO permissions bitmap exceeds the maximum length (8193). + TooLong { + len: usize + }, +} From 780a8d6b30fb6c8679fa0ddb32c6581b8446a022 Mon Sep 17 00:00:00 2001 From: Restioson Date: Sat, 17 Oct 2020 15:18:26 +0200 Subject: [PATCH 09/13] error if base =/= expected base --- src/structures/gdt.rs | 13 ++++++++++--- src/structures/tss.rs | 5 +++++ 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/structures/gdt.rs b/src/structures/gdt.rs index 63a105e02..f0a4f3350 100644 --- a/src/structures/gdt.rs +++ b/src/structures/gdt.rs @@ -320,9 +320,9 @@ impl Descriptor { return Err(InvalidIoMap::TooLong { len: iomap.len() }) } - let distance = iomap.as_ptr() as usize - tss as *const _ as usize; - if distance > 0xdfff { - return Err(InvalidIoMap::TooFarFromTss { distance }) + let base = iomap.as_ptr() as usize - tss as *const _ as usize; + if base > 0xdfff { + return Err(InvalidIoMap::TooFarFromTss { distance: base }) } let last_byte = *iomap.last().unwrap_or(&0xff); @@ -330,6 +330,13 @@ impl Descriptor { return Err(InvalidIoMap::InvalidTerminatingByte { byte: last_byte }) } + if tss.iomap_base != base as u16 { + return Err(InvalidIoMap::InvalidBase { + expected: base as u16, + got: tss.iomap_base + }); + } + // SAFETY: all invariants checked above Ok(unsafe { Self::tss_segment_raw(tss, iomap.len() as u16) }) } diff --git a/src/structures/tss.rs b/src/structures/tss.rs index 1d3178dd5..2d4d0df8d 100644 --- a/src/structures/tss.rs +++ b/src/structures/tss.rs @@ -55,4 +55,9 @@ pub enum InvalidIoMap { TooLong { len: usize }, + /// The `iomap_base` in the `TaskStateSegment` struct was not what was expected. + InvalidBase { + expected: u16, + got: u16, + } } From 25ce9aac4350f7999a3bc9fe1dd458e129033741 Mon Sep 17 00:00:00 2001 From: Restioson Date: Sat, 17 Oct 2020 15:19:03 +0200 Subject: [PATCH 10/13] fmt (i always forget this...) --- src/structures/gdt.rs | 15 ++++++--------- src/structures/tss.rs | 17 ++++------------- 2 files changed, 10 insertions(+), 22 deletions(-) diff --git a/src/structures/gdt.rs b/src/structures/gdt.rs index f0a4f3350..d91789b18 100644 --- a/src/structures/gdt.rs +++ b/src/structures/gdt.rs @@ -1,11 +1,11 @@ //! Types for the Global Descriptor Table and segment selectors. +use crate::structures::tss::InvalidIoMap; use crate::structures::{tss::TaskStateSegment, DescriptorTablePointer}; use crate::PrivilegeLevel; use bit_field::BitField; use bitflags::bitflags; use core::{cmp, fmt, mem}; -use crate::structures::tss::InvalidIoMap; /// Specifies which element to load into a segment from /// descriptor tables (i.e., is a index to LDT or GDT table @@ -317,23 +317,23 @@ impl Descriptor { iomap: &'static [u8], ) -> Result { if iomap.len() > 8193 { - return Err(InvalidIoMap::TooLong { len: iomap.len() }) + return Err(InvalidIoMap::TooLong { len: iomap.len() }); } let base = iomap.as_ptr() as usize - tss as *const _ as usize; if base > 0xdfff { - return Err(InvalidIoMap::TooFarFromTss { distance: base }) + return Err(InvalidIoMap::TooFarFromTss { distance: base }); } let last_byte = *iomap.last().unwrap_or(&0xff); if last_byte != 0xff { - return Err(InvalidIoMap::InvalidTerminatingByte { byte: last_byte }) + return Err(InvalidIoMap::InvalidTerminatingByte { byte: last_byte }); } if tss.iomap_base != base as u16 { return Err(InvalidIoMap::InvalidBase { expected: base as u16, - got: tss.iomap_base + got: tss.iomap_base, }); } @@ -348,10 +348,7 @@ impl Descriptor { /// There must be a valid IO map at `(tss as *const u8).offset(tss.iomap_base)` /// of length `iomap_size`, with the terminating `0xFF` byte. Additionally, `iomap_base` must /// not exceed `0xDFFF`. - unsafe fn tss_segment_raw( - tss: &'static TaskStateSegment, - iomap_size: u16, - ) -> Descriptor { + unsafe fn tss_segment_raw(tss: &'static TaskStateSegment, iomap_size: u16) -> Descriptor { use self::DescriptorFlags as Flags; let ptr = tss as *const _ as u64; diff --git a/src/structures/tss.rs b/src/structures/tss.rs index 2d4d0df8d..1cbb3b22d 100644 --- a/src/structures/tss.rs +++ b/src/structures/tss.rs @@ -44,20 +44,11 @@ impl TaskStateSegment { pub enum InvalidIoMap { /// The IO permissions bitmap is too far from the TSS. It must be within `0xdfff` bytes of the /// start of the TSS. - TooFarFromTss { - distance: usize, - }, + TooFarFromTss { distance: usize }, /// The final byte of the IO permissions bitmap was not 0xff - InvalidTerminatingByte { - byte: u8, - }, + InvalidTerminatingByte { byte: u8 }, /// The IO permissions bitmap exceeds the maximum length (8193). - TooLong { - len: usize - }, + TooLong { len: usize }, /// The `iomap_base` in the `TaskStateSegment` struct was not what was expected. - InvalidBase { - expected: u16, - got: u16, - } + InvalidBase { expected: u16, got: u16 }, } From 7691201c388db5afc478259527fe7628b53fd0ae Mon Sep 17 00:00:00 2001 From: Restioson Date: Sat, 17 Oct 2020 15:20:49 +0200 Subject: [PATCH 11/13] doc --- src/structures/tss.rs | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/src/structures/tss.rs b/src/structures/tss.rs index 1cbb3b22d..ce01ebea9 100644 --- a/src/structures/tss.rs +++ b/src/structures/tss.rs @@ -44,11 +44,25 @@ impl TaskStateSegment { pub enum InvalidIoMap { /// The IO permissions bitmap is too far from the TSS. It must be within `0xdfff` bytes of the /// start of the TSS. - TooFarFromTss { distance: usize }, + TooFarFromTss { + /// The distance of the IO permissions bitmap from the beginning of the TSS. + distance: usize + }, /// The final byte of the IO permissions bitmap was not 0xff - InvalidTerminatingByte { byte: u8 }, + InvalidTerminatingByte { + /// The byte found at the end of the IO permissions bitmap. + byte: u8 + }, /// The IO permissions bitmap exceeds the maximum length (8193). - TooLong { len: usize }, + TooLong { + /// The length of the IO permissions bitmap. + len: usize + }, /// The `iomap_base` in the `TaskStateSegment` struct was not what was expected. - InvalidBase { expected: u16, got: u16 }, + InvalidBase { + /// The expected `iomap_base` to be set in the `TaskStateSegment` struct. + expected: u16, + /// The actual `iomap_base` set in the `TaskStateSegment` struct. + got: u16 + }, } From fa5d40ec9fa6a29c44b2b3eaa72d92005e64186f Mon Sep 17 00:00:00 2001 From: Restioson Date: Sat, 17 Oct 2020 15:22:20 +0200 Subject: [PATCH 12/13] fmt --- src/structures/tss.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/structures/tss.rs b/src/structures/tss.rs index ce01ebea9..cd47b28f6 100644 --- a/src/structures/tss.rs +++ b/src/structures/tss.rs @@ -46,23 +46,23 @@ pub enum InvalidIoMap { /// start of the TSS. TooFarFromTss { /// The distance of the IO permissions bitmap from the beginning of the TSS. - distance: usize + distance: usize, }, /// The final byte of the IO permissions bitmap was not 0xff InvalidTerminatingByte { /// The byte found at the end of the IO permissions bitmap. - byte: u8 + byte: u8, }, /// The IO permissions bitmap exceeds the maximum length (8193). TooLong { /// The length of the IO permissions bitmap. - len: usize + len: usize, }, /// The `iomap_base` in the `TaskStateSegment` struct was not what was expected. InvalidBase { /// The expected `iomap_base` to be set in the `TaskStateSegment` struct. expected: u16, /// The actual `iomap_base` set in the `TaskStateSegment` struct. - got: u16 + got: u16, }, } From fa95830c98d2d534a5c060dbb28fcbf66a49fd1c Mon Sep 17 00:00:00 2001 From: Restioson Date: Sat, 17 Oct 2020 15:57:35 +0200 Subject: [PATCH 13/13] handle iomap before tss --- src/structures/gdt.rs | 9 ++++++++- src/structures/tss.rs | 5 ++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/structures/gdt.rs b/src/structures/gdt.rs index d91789b18..c1a40421e 100644 --- a/src/structures/gdt.rs +++ b/src/structures/gdt.rs @@ -320,7 +320,14 @@ impl Descriptor { return Err(InvalidIoMap::TooLong { len: iomap.len() }); } - let base = iomap.as_ptr() as usize - tss as *const _ as usize; + let iomap_addr = iomap.as_ptr() as usize; + let tss_addr = tss as *const _ as usize; + + if tss_addr > iomap_addr { + return Err(InvalidIoMap::IoMapBeforeTss); + } + + let base = iomap_addr - tss_addr; if base > 0xdfff { return Err(InvalidIoMap::TooFarFromTss { distance: base }); } diff --git a/src/structures/tss.rs b/src/structures/tss.rs index cd47b28f6..0013f0cd8 100644 --- a/src/structures/tss.rs +++ b/src/structures/tss.rs @@ -42,8 +42,11 @@ impl TaskStateSegment { /// The given IO permissions bitmap is invalid. #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum InvalidIoMap { + /// The IO permissions bitmap is before the TSS. It must be located after the TSS. + IoMapBeforeTss, /// The IO permissions bitmap is too far from the TSS. It must be within `0xdfff` bytes of the - /// start of the TSS. + /// start of the TSS. Note that if the IO permissions bitmap is located before the TSS, then + /// `IoMapBeforeTss` will be returned instead. TooFarFromTss { /// The distance of the IO permissions bitmap from the beginning of the TSS. distance: usize,