Skip to content

Commit e39bf03

Browse files
committed
Fix validation for Tcl/Tk
1 parent 390a158 commit e39bf03

File tree

4 files changed

+85
-10
lines changed

4 files changed

+85
-10
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: 72 additions & 9 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: "@rpath/libtcl8.6.dylib".to_string(),
531+
max_compatibility_version: "8.6.0".try_into().unwrap(),
532+
required: true,
533+
},
534+
MachOAllowedDylib {
535+
name: "@rpath/libtk8.6.dylib".to_string(),
536+
max_compatibility_version: "8.6.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"),
@@ -544,9 +586,12 @@ const ELF_BANNED_SYMBOLS: &[&str] = &[
544586
/// We use this list to spot test behavior of symbols belonging to dependency packages.
545587
/// The list is obviously not complete.
546588
const DEPENDENCY_PACKAGE_SYMBOLS: &[&str] = &[
547-
// libX11
548-
"XClearWindow",
549-
"XFlush",
589+
/* TODO(geofft): Tk provides these as no-op stubs on macOS, make it
590+
* stop doing that so we can reenable the check
591+
* // libX11
592+
* "XClearWindow",
593+
* "XFlush",
594+
*/
550595
// OpenSSL
551596
"BIO_ADDR_new",
552597
"BN_new",
@@ -591,6 +636,11 @@ const DEPENDENCY_PACKAGE_SYMBOLS: &[&str] = &[
591636
// liblzma
592637
"lzma_index_init",
593638
"lzma_stream_encoder",
639+
];
640+
641+
// TODO(geofft): Conditionally prohibit these exported symbols
642+
// everywhere except libtcl and libtk. This should be a hashmap
643+
const _DEPENDENCY_PACKAGE_SYMBOLS_BUNDLED: &[&str] = &[
594644
// tcl
595645
"Tcl_Alloc",
596646
"Tcl_ChannelName",
@@ -967,11 +1017,13 @@ fn validate_elf<Elf: FileHeader<Endian = Endianness>>(
9671017
allowed_libraries.push("libc.so".to_string());
9681018
}
9691019

970-
// Allow the _crypt extension module - and only it - to link against libcrypt,
971-
// which is no longer universally present in Linux distros.
1020+
// Allow certain extension modules to link against shared libraries
1021+
// (either from the system or from our distribution).
9721022
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());
1023+
if let Some((module, _)) = filename.to_string_lossy().split_once(".cpython-") {
1024+
if let Some(extra) = ELF_ALLOWED_LIBRARIES_BY_MODULE.get(module) {
1025+
allowed_libraries.extend(extra.iter().map(|x| x.to_string()));
1026+
}
9751027
}
9761028
}
9771029

@@ -1109,6 +1161,7 @@ fn validate_elf<Elf: FileHeader<Endian = Endianness>>(
11091161
// to prevent them from being exported.
11101162
if DEPENDENCY_PACKAGE_SYMBOLS.contains(&name.as_ref())
11111163
&& matches!(symbol.st_bind(), STB_GLOBAL | STB_WEAK)
1164+
&& symbol.st_shndx(endian) != SHN_UNDEF
11121165
&& symbol.st_visibility() != STV_HIDDEN
11131166
{
11141167
context.errors.push(format!(
@@ -1124,6 +1177,7 @@ fn validate_elf<Elf: FileHeader<Endian = Endianness>>(
11241177
if filename.starts_with("libpython")
11251178
&& filename.ends_with(".so.1.0")
11261179
&& matches!(symbol.st_bind(), STB_GLOBAL | STB_WEAK)
1180+
&& symbol.st_shndx(endian) != SHN_UNDEF
11271181
&& symbol.st_visibility() == STV_DEFAULT
11281182
{
11291183
context.libpython_exported_symbols.insert(name.to_string());
@@ -1225,7 +1279,16 @@ fn validate_macho<Mach: MachHeader<Endian = Endianness>>(
12251279

12261280
dylib_names.push(lib.clone());
12271281

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

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

0 commit comments

Comments
 (0)