Skip to content

Commit e81abb6

Browse files
committed
Fix validation for Tcl/Tk
1 parent 3910246 commit e81abb6

File tree

4 files changed

+79
-7
lines changed

4 files changed

+79
-7
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
name = "pythonbuild"
33
version = "0.1.0"
44
authors = ["Gregory Szorc <[email protected]>"]
5-
edition = "2021"
5+
edition = "2024"
66

77
[dependencies]
88
anyhow = "1.0.80"

cpython-unix/build-tcl.sh

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@ set -ex
77

88
ROOT=`pwd`
99

10+
# Force linking to static libraries from our dependencies.
11+
# TODO(geofft): This is copied from build-cpython.sh. Really this should
12+
# be done at the end of the build of each dependency, rather than before
13+
# the build of each consumer.
14+
find ${TOOLS_PATH}/deps -name '*.so*' -exec rm {} \;
15+
1016
export PATH=${TOOLS_PATH}/${TOOLCHAIN}/bin:${TOOLS_PATH}/host/bin:$PATH
1117
export PKG_CONFIG_PATH=${TOOLS_PATH}/deps/share/pkgconfig:${TOOLS_PATH}/deps/lib/pkgconfig
1218

cpython-unix/build-tk.sh

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@ set -ex
77

88
ROOT=`pwd`
99

10+
# Force linking to static libraries from our dependencies.
11+
# TODO(geofft): This is copied from build-cpython.sh. Really this should
12+
# be done at the end of the build of each dependency, rather than before
13+
# the build of each consumer.
14+
find ${TOOLS_PATH}/deps -name '*.so*' -exec rm {} \;
15+
1016
export PATH=${TOOLS_PATH}/deps/bin:${TOOLS_PATH}/${TOOLCHAIN}/bin:${TOOLS_PATH}/host/bin:$PATH
1117
export PKG_CONFIG_PATH=${TOOLS_PATH}/deps/share/pkgconfig:${TOOLS_PATH}/deps/lib/pkgconfig
1218

src/validation.rs

Lines changed: 66 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use {
99
normalize_path::NormalizePath,
1010
object::{
1111
elf::{
12-
FileHeader32, FileHeader64, ET_DYN, ET_EXEC, STB_GLOBAL, STB_WEAK, STV_DEFAULT,
12+
FileHeader32, FileHeader64, ET_DYN, ET_EXEC, SHN_UNDEF, STB_GLOBAL, STB_WEAK, STV_DEFAULT,
1313
STV_HIDDEN,
1414
},
1515
macho::{MachHeader32, MachHeader64, MH_OBJECT, MH_TWOLEVEL},
@@ -265,6 +265,25 @@ static ELF_ALLOWED_LIBRARIES_BY_TRIPLE: Lazy<HashMap<&'static str, Vec<&'static
265265
.collect()
266266
});
267267

268+
static ELF_ALLOWED_LIBRARIES_BY_MODULE: Lazy<HashMap<&'static str, Vec<&'static str>>> =
269+
Lazy::new(|| {
270+
[
271+
(
272+
// libcrypt is provided by the system, but only on older distros.
273+
"_crypt",
274+
vec!["libcrypt.so.1"],
275+
),
276+
(
277+
// libtcl and libtk are shipped in our distribution.
278+
"_tkinter",
279+
vec!["libtcl8.6.so", "libtk8.6.so"],
280+
),
281+
]
282+
.iter()
283+
.cloned()
284+
.collect()
285+
});
286+
268287
static DARWIN_ALLOWED_DYLIBS: Lazy<Vec<MachOAllowedDylib>> = Lazy::new(|| {
269288
[
270289
MachOAllowedDylib {
@@ -501,6 +520,29 @@ static IOS_ALLOWED_DYLIBS: Lazy<Vec<MachOAllowedDylib>> = Lazy::new(|| {
501520
.to_vec()
502521
});
503522

523+
static ALLOWED_DYLIBS_BY_MODULE: Lazy<HashMap<&'static str, Vec<MachOAllowedDylib>>> =
524+
Lazy::new(|| {
525+
[(
526+
// libtcl and libtk are shipped in our distribution.
527+
"_tkinter",
528+
vec![
529+
MachOAllowedDylib {
530+
name: "libtcl8.6.dylib".to_string(),
531+
max_compatibility_version: "1.0.0".try_into().unwrap(),
532+
required: true,
533+
},
534+
MachOAllowedDylib {
535+
name: "libtk8.6.dylib".to_string(),
536+
max_compatibility_version: "1.0.0".try_into().unwrap(),
537+
required: true,
538+
},
539+
],
540+
)]
541+
.iter()
542+
.cloned()
543+
.collect()
544+
});
545+
504546
static PLATFORM_TAG_BY_TRIPLE: Lazy<HashMap<&'static str, &'static str>> = Lazy::new(|| {
505547
[
506548
("aarch64-apple-darwin", "macosx-11.0-arm64"),
@@ -591,6 +633,11 @@ const DEPENDENCY_PACKAGE_SYMBOLS: &[&str] = &[
591633
// liblzma
592634
"lzma_index_init",
593635
"lzma_stream_encoder",
636+
];
637+
638+
// TODO(geofft): Conditionally prohibit these exported symbols
639+
// everywhere except libtcl and libtk. This should be a hashmap
640+
const _DEPENDENCY_PACKAGE_SYMBOLS_BUNDLED: &[&str] = &[
594641
// tcl
595642
"Tcl_Alloc",
596643
"Tcl_ChannelName",
@@ -967,11 +1014,13 @@ fn validate_elf<Elf: FileHeader<Endian = Endianness>>(
9671014
allowed_libraries.push("libc.so".to_string());
9681015
}
9691016

970-
// Allow the _crypt extension module - and only it - to link against libcrypt,
971-
// which is no longer universally present in Linux distros.
1017+
// Allow certain extension modules to link against shared libraries
1018+
// (either from the system or from our distribution).
9721019
if let Some(filename) = path.file_name() {
973-
if filename.to_string_lossy().starts_with("_crypt") {
974-
allowed_libraries.push("libcrypt.so.1".to_string());
1020+
if let Some((module, _)) = filename.to_string_lossy().split_once(".cpython-") {
1021+
if let Some(extra) = ELF_ALLOWED_LIBRARIES_BY_MODULE.get(module) {
1022+
allowed_libraries.extend(extra.iter().map(|x| x.to_string()));
1023+
}
9751024
}
9761025
}
9771026

@@ -1109,6 +1158,7 @@ fn validate_elf<Elf: FileHeader<Endian = Endianness>>(
11091158
// to prevent them from being exported.
11101159
if DEPENDENCY_PACKAGE_SYMBOLS.contains(&name.as_ref())
11111160
&& matches!(symbol.st_bind(), STB_GLOBAL | STB_WEAK)
1161+
&& symbol.st_shndx(endian) != SHN_UNDEF
11121162
&& symbol.st_visibility() != STV_HIDDEN
11131163
{
11141164
context.errors.push(format!(
@@ -1124,6 +1174,7 @@ fn validate_elf<Elf: FileHeader<Endian = Endianness>>(
11241174
if filename.starts_with("libpython")
11251175
&& filename.ends_with(".so.1.0")
11261176
&& matches!(symbol.st_bind(), STB_GLOBAL | STB_WEAK)
1177+
&& symbol.st_shndx(endian) != SHN_UNDEF
11271178
&& symbol.st_visibility() == STV_DEFAULT
11281179
{
11291180
context.libpython_exported_symbols.insert(name.to_string());
@@ -1225,7 +1276,16 @@ fn validate_macho<Mach: MachHeader<Endian = Endianness>>(
12251276

12261277
dylib_names.push(lib.clone());
12271278

1228-
let allowed = allowed_dylibs_for_triple(target_triple);
1279+
let mut allowed = allowed_dylibs_for_triple(target_triple);
1280+
// Allow certain extension modules to link against shared libraries
1281+
// (either from the system or from our distribution).
1282+
if let Some(filename) = path.file_name() {
1283+
if let Some((module, _)) = filename.to_string_lossy().split_once(".cpython-") {
1284+
if let Some(extra) = ALLOWED_DYLIBS_BY_MODULE.get(module) {
1285+
allowed.extend(extra.clone());
1286+
}
1287+
}
1288+
}
12291289

12301290
if let Some(entry) = allowed.iter().find(|l| l.name == lib) {
12311291
let load_version =

0 commit comments

Comments
 (0)