Skip to content

Commit 5966628

Browse files
committed
rust: validate all linked ELF libraries have annotations
We added the proper annotations a few commits ago. Here we teach the validation code to check for it. Only ELF for the moment because macOS and Windows generally aren't as big of a problem when it comes to this.
1 parent 63c54c2 commit 5966628

File tree

1 file changed

+80
-8
lines changed

1 file changed

+80
-8
lines changed

src/validation.rs

Lines changed: 80 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -401,12 +401,29 @@ fn allowed_dylibs_for_triple(triple: &str) -> Vec<MachOAllowedDylib> {
401401
}
402402

403403
fn validate_elf(
404+
json: &PythonJsonMain,
404405
target_triple: &str,
405406
python_major_minor: &str,
406407
path: &Path,
407408
elf: &goblin::elf::Elf,
408409
bytes: &[u8],
409410
) -> Result<Vec<String>> {
411+
let mut system_links = BTreeSet::new();
412+
for link in &json.build_info.core.links {
413+
if link.system.unwrap_or_default() {
414+
system_links.insert(link.name.as_str());
415+
}
416+
}
417+
for extension in json.build_info.extensions.values() {
418+
for variant in extension {
419+
for link in &variant.links {
420+
if link.system.unwrap_or_default() {
421+
system_links.insert(link.name.as_str());
422+
}
423+
}
424+
}
425+
}
426+
410427
let mut errors = vec![];
411428

412429
let wanted_cpu_type = match target_triple {
@@ -453,6 +470,47 @@ fn validate_elf(
453470
if !allowed_libraries.contains(&lib.to_string()) {
454471
errors.push(format!("{} loads illegal library {}", path.display(), lib));
455472
}
473+
474+
// Most linked libraries should have an annotation in the JSON metadata.
475+
let requires_annotation = !lib.contains("libpython")
476+
&& !lib.starts_with("ld-linux")
477+
&& !lib.starts_with("ld64")
478+
&& !lib.starts_with("libc")
479+
&& !lib.starts_with("libgcc_s");
480+
481+
if requires_annotation {
482+
if lib.starts_with("lib") {
483+
if let Some(index) = lib.rfind(".so") {
484+
let lib_name = &lib[3..index];
485+
486+
// There should be a system links entry for this library in the JSON
487+
// metadata.
488+
//
489+
// Nominally we would look at where this ELF came from and make sure
490+
// the annotation is present in its section (e.g. core or extension).
491+
// But this is more work.
492+
if !system_links.contains(lib_name) {
493+
errors.push(format!(
494+
"{} library load of {} does not have system link build annotation",
495+
path.display(),
496+
lib
497+
));
498+
}
499+
} else {
500+
errors.push(format!(
501+
"{} library load of {} does not have .so extension",
502+
path.display(),
503+
lib
504+
));
505+
}
506+
} else {
507+
errors.push(format!(
508+
"{} library load of {} does not begin with lib",
509+
path.display(),
510+
lib
511+
));
512+
}
513+
}
456514
}
457515

458516
let wanted_glibc_max_version = GLIBC_MAX_VERSION_BY_TRIPLE
@@ -581,6 +639,7 @@ fn validate_pe(path: &Path, pe: &goblin::pe::PE) -> Result<Vec<String>> {
581639

582640
/// Attempt to parse data as an object file and validate it.
583641
fn validate_possible_object_file(
642+
json: &PythonJsonMain,
584643
python_major_minor: &str,
585644
triple: &str,
586645
path: &Path,
@@ -593,6 +652,7 @@ fn validate_possible_object_file(
593652
match object {
594653
goblin::Object::Elf(elf) => {
595654
errors.extend(validate_elf(
655+
json,
596656
triple,
597657
python_major_minor,
598658
path.as_ref(),
@@ -727,17 +787,24 @@ fn validate_distribution(dist_path: &Path) -> Result<Vec<String>> {
727787
let mut entries = tf.entries()?;
728788

729789
let mut wanted_python_paths = BTreeSet::new();
790+
let mut json = None;
730791

731792
let mut entry = entries.next().unwrap()?;
732793
if entry.path()?.display().to_string() == "python/PYTHON.json" {
733794
seen_paths.insert(entry.path()?.to_path_buf());
734795

735796
let mut data = Vec::new();
736797
entry.read_to_end(&mut data)?;
737-
let json = parse_python_json(&data).context("parsing PYTHON.json")?;
738-
errors.extend(validate_json(&json, triple, is_debug)?);
739-
740-
wanted_python_paths.extend(json.python_paths.values().map(|x| format!("python/{}", x)));
798+
json = Some(parse_python_json(&data).context("parsing PYTHON.json")?);
799+
errors.extend(validate_json(json.as_ref().unwrap(), triple, is_debug)?);
800+
801+
wanted_python_paths.extend(
802+
json.as_ref()
803+
.unwrap()
804+
.python_paths
805+
.values()
806+
.map(|x| format!("python/{}", x)),
807+
);
741808
} else {
742809
errors.push(format!(
743810
"1st archive entry should be for python/PYTHON.json; got {}",
@@ -770,8 +837,13 @@ fn validate_distribution(dist_path: &Path) -> Result<Vec<String>> {
770837
let mut data = Vec::new();
771838
entry.read_to_end(&mut data)?;
772839

773-
let (local_errors, local_seen_dylibs) =
774-
validate_possible_object_file(python_major_minor, &triple, &path, &data)?;
840+
let (local_errors, local_seen_dylibs) = validate_possible_object_file(
841+
json.as_ref().unwrap(),
842+
python_major_minor,
843+
&triple,
844+
&path,
845+
&data,
846+
)?;
775847
errors.extend(local_errors);
776848
seen_dylibs.extend(local_seen_dylibs);
777849

@@ -790,6 +862,7 @@ fn validate_distribution(dist_path: &Path) -> Result<Vec<String>> {
790862
));
791863

792864
let (local_errors, local_seen_dylibs) = validate_possible_object_file(
865+
json.as_ref().unwrap(),
793866
python_major_minor,
794867
&triple,
795868
&member_path,
@@ -801,8 +874,7 @@ fn validate_distribution(dist_path: &Path) -> Result<Vec<String>> {
801874
}
802875

803876
if path == PathBuf::from("python/PYTHON.json") {
804-
let json = parse_python_json(&data).context("parsing PYTHON.json")?;
805-
errors.extend(validate_json(&json, triple, is_debug)?);
877+
errors.push("python/PYTHON.json seen twice".to_string());
806878
}
807879
}
808880

0 commit comments

Comments
 (0)