···4848 | Error _ -> fail "failed to create partition"
4949 in
5050 let gpt =
5151- match Gpt.make ~disk_sectors:2097152L ~sector_size:512 [ p1 ] with
5151+ match Gpt.v ~disk_sectors:2097152L ~sector_size:512 [ p1 ] with
5252 | Ok g -> g
5353 | Error _ -> fail "failed to create GPT"
5454 in
···8989 match p1 with
9090 | Error _ -> ()
9191 | Ok p1 ->
9292- let _ = Gpt.make ~disk_sectors ~sector_size [ p1 ] in
9292+ let _ = Gpt.v ~disk_sectors ~sector_size [ p1 ] in
9393 ()
94949595(** CVE-2011-1776 pattern: GPT with crafted num_partition_entries. Parser must
+132-159
lib/gpt.ml
···147147 partitions_crc32 : int32;
148148}
149149150150+let make_raw_header _sig revision header_size header_crc32 reserved current_lba
151151+ backup_lba first_usable_lba last_usable_lba disk_guid partition_entry_lba
152152+ num_partition_entries partition_size partitions_crc32 =
153153+ {
154154+ revision;
155155+ header_size;
156156+ header_crc32;
157157+ reserved;
158158+ current_lba;
159159+ backup_lba;
160160+ first_usable_lba;
161161+ last_usable_lba;
162162+ disk_guid;
163163+ partition_entry_lba;
164164+ num_partition_entries;
165165+ partition_size;
166166+ partitions_crc32;
167167+ }
168168+150169let header_codec =
151170 let open Wire.Codec in
152152- record "GptHeader"
153153- (fun
154154- _sig
155155- revision
156156- header_size
157157- header_crc32
158158- reserved
159159- current_lba
160160- backup_lba
161161- first_usable_lba
162162- last_usable_lba
163163- disk_guid
164164- partition_entry_lba
165165- num_partition_entries
166166- partition_size
167167- partitions_crc32
168168- ->
169169- {
170170- revision;
171171- header_size;
172172- header_crc32;
173173- reserved;
174174- current_lba;
175175- backup_lba;
176176- first_usable_lba;
177177- last_usable_lba;
178178- disk_guid;
179179- partition_entry_lba;
180180- num_partition_entries;
181181- partition_size;
182182- partitions_crc32;
183183- })
171171+ record "GptHeader" make_raw_header
184172 |+ field "signature"
185173 (Wire.byte_array ~size:(Wire.int 8))
186174 (fun _ -> "EFI PART")
···219207 partitions : Partition.t list;
220208}
221209210210+let pp ppf t =
211211+ Format.fprintf ppf "@[<v>GPT rev=%lx disk=%a lba=[%Ld..%Ld] partitions=%d@]"
212212+ t.revision Uuidm.pp t.disk_guid t.first_usable_lba t.last_usable_lba
213213+ (List.length t.partitions)
214214+222215let signature = "EFI PART"
223216224217let raw_header_of_t t =
···268261 in
269262 loop crc (num_partitions - List.length partitions)
270263271271-let make ?disk_guid ~disk_sectors ~sector_size partitions =
272272- (* Security: validate sector_size *)
273273- let* () =
274274- if sector_size < min_sector_size || sector_size > max_sector_size then
275275- Error
276276- (Printf.sprintf "sector_size %d out of range [%d, %d]" sector_size
277277- min_sector_size max_sector_size)
278278- else if sector_size land (sector_size - 1) <> 0 then
279279- Error (Printf.sprintf "sector_size %d must be a power of 2" sector_size)
280280- else Ok ()
264264+let validate_sector_size sector_size =
265265+ if sector_size < min_sector_size || sector_size > max_sector_size then
266266+ Error
267267+ (Printf.sprintf "sector_size %d out of range [%d, %d]" sector_size
268268+ min_sector_size max_sector_size)
269269+ else if sector_size land (sector_size - 1) <> 0 then
270270+ Error (Printf.sprintf "sector_size %d must be a power of 2" sector_size)
271271+ else Ok ()
272272+273273+let validate_partition_lbas ~first_usable_lba ~last_usable_lba partitions =
274274+ List.fold_left
275275+ (fun r p ->
276276+ let* () = r in
277277+ if Int64.compare p.Partition.starting_lba first_usable_lba < 0 then
278278+ Error
279279+ (Printf.sprintf
280280+ "Partition starting_lba %Ld is before first_usable_lba %Ld"
281281+ p.Partition.starting_lba first_usable_lba)
282282+ else if Int64.compare p.Partition.ending_lba last_usable_lba > 0 then
283283+ Error
284284+ (Printf.sprintf "Partition ending_lba %Ld exceeds last_usable_lba %Ld"
285285+ p.Partition.ending_lba last_usable_lba)
286286+ else if Int64.compare p.Partition.starting_lba p.Partition.ending_lba > 0
287287+ then
288288+ Error
289289+ (Printf.sprintf "Partition starting_lba %Ld > ending_lba %Ld"
290290+ p.Partition.starting_lba p.Partition.ending_lba)
291291+ else Ok ())
292292+ (Ok ()) partitions
293293+294294+let check_partition_overlap ~first_usable_lba partitions =
295295+ let* _last_partition_lba =
296296+ List.fold_left
297297+ (fun r p ->
298298+ let* offset = r in
299299+ if Int64.unsigned_compare offset p.Partition.starting_lba <= 0 then
300300+ Ok p.Partition.ending_lba
301301+ else Error "Partitions overlap")
302302+ (Ok first_usable_lba) partitions
281303 in
304304+ Ok ()
305305+306306+let v ?disk_guid ~disk_sectors ~sector_size partitions =
307307+ let* () = validate_sector_size sector_size in
282308 (* Security: validate disk_sectors is positive *)
283309 let* () =
284310 if Int64.compare disk_sectors 0L <= 0 then
···310336 Int64.(add partition_entry_lba (of_int partition_table_sectors))
311337 in
312338 let last_usable_lba = Int64.sub disk_sectors 2L in
313313- (* Security: validate partition LBAs are within disk bounds *)
314339 let* () =
315315- List.fold_left
316316- (fun r p ->
317317- let* () = r in
318318- if Int64.compare p.Partition.starting_lba first_usable_lba < 0 then
319319- Error
320320- (Printf.sprintf
321321- "Partition starting_lba %Ld is before first_usable_lba %Ld"
322322- p.Partition.starting_lba first_usable_lba)
323323- else if Int64.compare p.Partition.ending_lba last_usable_lba > 0 then
324324- Error
325325- (Printf.sprintf
326326- "Partition ending_lba %Ld exceeds last_usable_lba %Ld"
327327- p.Partition.ending_lba last_usable_lba)
328328- else if
329329- Int64.compare p.Partition.starting_lba p.Partition.ending_lba > 0
330330- then
331331- Error
332332- (Printf.sprintf "Partition starting_lba %Ld > ending_lba %Ld"
333333- p.Partition.starting_lba p.Partition.ending_lba)
334334- else Ok ())
335335- (Ok ()) partitions
340340+ validate_partition_lbas ~first_usable_lba ~last_usable_lba partitions
336341 in
337337- (* Check for overlapping partitions *)
338338- let* _last_partition_lba =
339339- List.fold_left
340340- (fun r p ->
341341- let* offset = r in
342342- if Int64.unsigned_compare offset p.Partition.starting_lba <= 0 then
343343- Ok p.Partition.ending_lba
344344- else Error "Partitions overlap")
345345- (Ok first_usable_lba) partitions
346346- in
342342+ let* () = check_partition_overlap ~first_usable_lba partitions in
347343 let current_lba = 1L in
348344 let backup_lba = Int64.sub disk_sectors 1L in
349345 let disk_guid =
···379375 let header_crc32 = Optint.to_int32 (calculate_header_crc32 header) in
380376 Ok { header with header_crc32 }
381377378378+let validate_header_crc32 buf (raw : raw_header) =
379379+ let header_size_int = Int32.to_int raw.header_size in
380380+ let buf_str = Bytes.to_string buf in
381381+ let crc32 =
382382+ Checkseum.Crc32.digest_string buf_str 0 16 Checkseum.Crc32.default
383383+ in
384384+ let crc32 = Checkseum.Crc32.digest_string "\000\000\000\000" 0 4 crc32 in
385385+ let crc32 =
386386+ Checkseum.Crc32.digest_string buf_str 20 (header_size_int - 20) crc32
387387+ in
388388+ let header_crc32' = Checkseum.Crc32.to_int32 crc32 in
389389+ if header_crc32' = raw.header_crc32 then Ok ()
390390+ else Error "Bad GPT header checksum"
391391+392392+let parse_partition_table (raw : raw_header) num_partition_entries_int table_buf
393393+ =
394394+ let table_size = num_partition_entries_int * Partition.sizeof in
395395+ if Bytes.length table_buf < table_size then
396396+ Printf.ksprintf invalid_arg "partition table buffer too small";
397397+ let table_str = Bytes.sub_string table_buf 0 table_size in
398398+ let partitions_crc32' =
399399+ Checkseum.Crc32.digest_string table_str 0 table_size Checkseum.Crc32.default
400400+ |> Checkseum.Crc32.to_int32
401401+ in
402402+ let* () =
403403+ if Int32.equal partitions_crc32' raw.partitions_crc32 then Ok ()
404404+ else Error "Bad partition table checksum"
405405+ in
406406+ let rev_partitions =
407407+ List.fold_left
408408+ (fun acc i ->
409409+ let entry = Partition.unmarshal table_buf (i * Partition.sizeof) in
410410+ if Partition.is_zero_partition entry then acc else entry :: acc)
411411+ []
412412+ (List.init num_partition_entries_int Fun.id)
413413+ in
414414+ let partitions = List.rev rev_partitions in
415415+ (* Security: validate partition LBAs are within usable range *)
416416+ let* () =
417417+ validate_partition_lbas ~first_usable_lba:raw.first_usable_lba
418418+ ~last_usable_lba:raw.last_usable_lba partitions
419419+ in
420420+ Ok
421421+ {
422422+ revision = raw.revision;
423423+ header_size = raw.header_size;
424424+ header_crc32 = raw.header_crc32;
425425+ reserved = raw.reserved;
426426+ current_lba = raw.current_lba;
427427+ backup_lba = raw.backup_lba;
428428+ first_usable_lba = raw.first_usable_lba;
429429+ last_usable_lba = raw.last_usable_lba;
430430+ disk_guid = raw.disk_guid;
431431+ partition_entry_lba = raw.partition_entry_lba;
432432+ num_partition_entries = raw.num_partition_entries;
433433+ partitions;
434434+ partition_size = raw.partition_size;
435435+ partitions_crc32 = raw.partitions_crc32;
436436+ }
437437+382438let unmarshal_bytes buf ~sector_size =
383439 if Bytes.length buf < sizeof then
384440 Printf.ksprintf invalid_arg "GPT too small: %d < %d" (Bytes.length buf)
···401457 (Printf.sprintf "Unknown revision; expected 0x10000, got 0x%08lx"
402458 raw.revision)
403459 in
404404- (* Validate header CRC32 (CRC field at offset 16 is zeroed for calculation) *)
405405- let* () =
406406- let header_size_int = Int32.to_int raw.header_size in
407407- let buf_str = Bytes.to_string buf in
408408- let crc32 =
409409- Checkseum.Crc32.digest_string buf_str 0 16 Checkseum.Crc32.default
410410- in
411411- let crc32 = Checkseum.Crc32.digest_string "\000\000\000\000" 0 4 crc32 in
412412- let crc32 =
413413- Checkseum.Crc32.digest_string buf_str 20 (header_size_int - 20) crc32
414414- in
415415- let header_crc32' = Checkseum.Crc32.to_int32 crc32 in
416416- if header_crc32' = raw.header_crc32 then Ok ()
417417- else Error "Bad GPT header checksum"
418418- in
460460+ let* () = validate_header_crc32 buf raw in
419461 (* Security: validate LBA ordering *)
420462 let* () =
421463 if Int64.compare raw.first_usable_lba raw.last_usable_lba > 0 then
···447489 in
448490 Ok
449491 ( `Read_partition_table (raw.partition_entry_lba, partition_entry_sectors),
450450- fun table_buf ->
451451- let table_size = num_partition_entries_int * Partition.sizeof in
452452- if Bytes.length table_buf < table_size then
453453- Printf.ksprintf invalid_arg "partition table buffer too small";
454454- let table_str = Bytes.sub_string table_buf 0 table_size in
455455- let partitions_crc32' =
456456- Checkseum.Crc32.digest_string table_str 0 table_size
457457- Checkseum.Crc32.default
458458- |> Checkseum.Crc32.to_int32
459459- in
460460- let* () =
461461- if Int32.equal partitions_crc32' raw.partitions_crc32 then Ok ()
462462- else Error "Bad partition table checksum"
463463- in
464464- let rev_partitions =
465465- List.fold_left
466466- (fun acc i ->
467467- let entry =
468468- Partition.unmarshal table_buf (i * Partition.sizeof)
469469- in
470470- if Partition.is_zero_partition entry then acc else entry :: acc)
471471- []
472472- (List.init num_partition_entries_int Fun.id)
473473- in
474474- let partitions = List.rev rev_partitions in
475475- (* Security: validate partition LBAs are within usable range *)
476476- let* () =
477477- List.fold_left
478478- (fun r p ->
479479- let* () = r in
480480- if Int64.compare p.Partition.starting_lba raw.first_usable_lba < 0
481481- then
482482- Error
483483- (Printf.sprintf
484484- "Partition starting_lba %Ld < first_usable_lba %Ld"
485485- p.Partition.starting_lba raw.first_usable_lba)
486486- else if
487487- Int64.compare p.Partition.ending_lba raw.last_usable_lba > 0
488488- then
489489- Error
490490- (Printf.sprintf
491491- "Partition ending_lba %Ld > last_usable_lba %Ld"
492492- p.Partition.ending_lba raw.last_usable_lba)
493493- else if
494494- Int64.compare p.Partition.starting_lba p.Partition.ending_lba
495495- > 0
496496- then
497497- Error
498498- (Printf.sprintf "Partition starting_lba %Ld > ending_lba %Ld"
499499- p.Partition.starting_lba p.Partition.ending_lba)
500500- else Ok ())
501501- (Ok ()) partitions
502502- in
503503- Ok
504504- {
505505- revision = raw.revision;
506506- header_size = raw.header_size;
507507- header_crc32 = raw.header_crc32;
508508- reserved = raw.reserved;
509509- current_lba = raw.current_lba;
510510- backup_lba = raw.backup_lba;
511511- first_usable_lba = raw.first_usable_lba;
512512- last_usable_lba = raw.last_usable_lba;
513513- disk_guid = raw.disk_guid;
514514- partition_entry_lba = raw.partition_entry_lba;
515515- num_partition_entries = raw.num_partition_entries;
516516- partitions;
517517- partition_size = raw.partition_size;
518518- partitions_crc32 = raw.partitions_crc32;
519519- } )
492492+ parse_partition_table raw num_partition_entries_int )
520493521494let of_string s ~sector_size = unmarshal_bytes (Bytes.of_string s) ~sector_size
522495
+6-3
lib/gpt.mli
···9696}
9797(** A GPT header with partition table. *)
98989999-val make :
9999+val pp : Format.formatter -> t -> unit
100100+(** [pp fmt t] pretty-prints the GPT header. *)
101101+102102+val v :
100103 ?disk_guid:Uuidm.t ->
101104 disk_sectors:int64 ->
102105 sector_size:int ->
103106 Partition.t list ->
104107 (t, string) result
105105-(** [make ?disk_guid ~disk_sectors ~sector_size partitions] constructs a GPT.
106106- The header is written at LBA 1, partition table from LBA 2 with 128 entries.
108108+(** [v ?disk_guid ~disk_sectors ~sector_size partitions] constructs a GPT. The
109109+ header is written at LBA 1, partition table from LBA 2 with 128 entries.
107110 Returns [Error _] if partitions exceed 128 or overlap. *)
108111109112val sizeof : int