@@ -93,6 +93,15 @@ impl ExeInfo {
9393 ExeInfo :: Elf ( elf) => elf. get_va_size ( ) ,
9494 }
9595 }
96+
97+ /// Returns the hyperlight version string embedded in the guest binary, if
98+ /// the binary was built with a version of `hyperlight-guest-bin` that
99+ /// supports version tagging.
100+ pub fn guest_bin_version ( & self ) -> Option < & str > {
101+ match self {
102+ ExeInfo :: Elf ( elf) => elf. guest_bin_version ( ) ,
103+ }
104+ }
96105 // todo: this doesn't morally need to be &mut self, since we're
97106 // copying into target, but the PE loader chooses to apply
98107 // relocations in its owned representation of the PE contents,
@@ -103,3 +112,110 @@ impl ExeInfo {
103112 }
104113 }
105114}
115+
116+ #[ cfg( test) ]
117+ mod tests {
118+ use hyperlight_testing:: { dummy_guest_as_string, simple_guest_as_string} ;
119+
120+ use super :: ExeInfo ;
121+
122+ /// Read the simpleguest binary and patch its version section to `"0.0.0"`.
123+ fn simpleguest_with_patched_version ( ) -> Vec < u8 > {
124+ let path = simple_guest_as_string ( ) . expect ( "failed to locate simpleguest" ) ;
125+ let mut bytes = std:: fs:: read ( path) . expect ( "failed to read simpleguest" ) ;
126+
127+ let elf = goblin:: elf:: Elf :: parse ( & bytes) . expect ( "failed to parse ELF" ) ;
128+ let sh = elf
129+ . section_headers
130+ . iter ( )
131+ . find ( |sh| {
132+ elf. shdr_strtab . get_at ( sh. sh_name )
133+ == Some ( hyperlight_common:: HYPERLIGHT_GUEST_BIN_VERSION_SECTION )
134+ } )
135+ . expect ( "version section should exist" ) ;
136+
137+ let start = sh. sh_offset as usize ;
138+ let fake_version = b"0.0.0\0 " ;
139+ assert ! (
140+ fake_version. len( ) <= sh. sh_size as usize ,
141+ "fake version must fit in the existing section"
142+ ) ;
143+ bytes[ start..start + fake_version. len ( ) ] . copy_from_slice ( fake_version) ;
144+ bytes
145+ }
146+
147+ #[ test]
148+ fn exe_info_exposes_guest_bin_version ( ) {
149+ let path = simple_guest_as_string ( ) . expect ( "failed to locate simpleguest" ) ;
150+ let info = ExeInfo :: from_file ( & path) . expect ( "failed to load ELF" ) ;
151+
152+ let version = info
153+ . guest_bin_version ( )
154+ . expect ( "simpleguest should have a version section" ) ;
155+ assert_eq ! ( version, env!( "CARGO_PKG_VERSION" ) ) ;
156+ }
157+
158+ #[ test]
159+ fn dummyguest_has_no_version_section ( ) {
160+ let path = dummy_guest_as_string ( ) . expect ( "failed to locate dummyguest" ) ;
161+ let info = ExeInfo :: from_file ( & path) . expect ( "failed to load ELF" ) ;
162+
163+ assert ! (
164+ info. guest_bin_version( ) . is_none( ) ,
165+ "dummyguest should not have a version section"
166+ ) ;
167+ }
168+
169+ /// Patch the version section in-memory to simulate a version mismatch.
170+ #[ test]
171+ fn patched_version_reports_mismatch ( ) {
172+ let bytes = simpleguest_with_patched_version ( ) ;
173+
174+ let info = ExeInfo :: from_buf ( & bytes) . expect ( "failed to load patched ELF" ) ;
175+ assert_eq ! ( info. guest_bin_version( ) , Some ( "0.0.0" ) ) ;
176+ assert_ne ! (
177+ info. guest_bin_version( ) . unwrap( ) ,
178+ env!( "CARGO_PKG_VERSION" ) ,
179+ "patched version should differ from host version"
180+ ) ;
181+ }
182+
183+ /// Load an unpatched simpleguest through `Snapshot::from_env` and verify
184+ /// that it succeeds when the embedded version matches the host version.
185+ #[ test]
186+ fn from_env_accepts_matching_version ( ) {
187+ let path = simple_guest_as_string ( ) . expect ( "failed to locate simpleguest" ) ;
188+
189+ let result = crate :: sandbox:: snapshot:: Snapshot :: from_env (
190+ crate :: GuestBinary :: FilePath ( path) ,
191+ crate :: sandbox:: SandboxConfiguration :: default ( ) ,
192+ ) ;
193+
194+ assert ! ( result. is_ok( ) , "should accept matching version" ) ;
195+ }
196+
197+ /// Load a patched guest binary through `Snapshot::from_env` and verify
198+ /// that a version mismatch produces `GuestBinVersionMismatch`.
199+ #[ test]
200+ fn from_env_rejects_version_mismatch ( ) {
201+ let bytes = simpleguest_with_patched_version ( ) ;
202+
203+ let result = crate :: sandbox:: snapshot:: Snapshot :: from_env (
204+ crate :: GuestBinary :: Buffer ( & bytes) ,
205+ crate :: sandbox:: SandboxConfiguration :: default ( ) ,
206+ ) ;
207+
208+ assert ! ( result. is_err( ) , "should reject mismatched version" ) ;
209+ let err = result. err ( ) . expect ( "already checked is_err" ) ;
210+ assert ! (
211+ matches!(
212+ err,
213+ crate :: HyperlightError :: GuestBinVersionMismatch {
214+ ref guest_bin_version,
215+ ref host_version,
216+ } if guest_bin_version == "0.0.0" && host_version == env!( "CARGO_PKG_VERSION" )
217+ ) ,
218+ "expected GuestBinVersionMismatch, got: {err}"
219+ ) ;
220+ }
221+ }
0 commit comments