Skip to content

Commit af819c0

Browse files
committed
Fix validation for Tcl/Tk
1 parent 8730151 commit af819c0

File tree

4 files changed

+80
-9
lines changed

4 files changed

+80
-9
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: 67 additions & 8 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,30 @@ 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+
(
527+
// libtcl and libtk are shipped in our distribution.
528+
"_tkinter",
529+
vec![
530+
MachOAllowedDylib {
531+
name: "libtcl8.6.dylib".to_string(),
532+
max_compatibility_version: "1.0.0".try_into().unwrap(),
533+
required: true,
534+
},
535+
MachOAllowedDylib {
536+
name: "libtk8.6.dylib".to_string(),
537+
max_compatibility_version: "1.0.0".try_into().unwrap(),
538+
required: true,
539+
}],
540+
),
541+
]
542+
.iter()
543+
.cloned()
544+
.collect()
545+
});
546+
504547
static PLATFORM_TAG_BY_TRIPLE: Lazy<HashMap<&'static str, &'static str>> = Lazy::new(|| {
505548
[
506549
("aarch64-apple-darwin", "macosx-11.0-arm64"),
@@ -591,6 +634,11 @@ const DEPENDENCY_PACKAGE_SYMBOLS: &[&str] = &[
591634
// liblzma
592635
"lzma_index_init",
593636
"lzma_stream_encoder",
637+
];
638+
639+
// TODO(geofft): Conditionally prohibit these exported symbols
640+
// everywhere except libtcl and libtk. This should be a hashmap
641+
const _DEPENDENCY_PACKAGE_SYMBOLS_BUNDLED: &[&str] = &[
594642
// tcl
595643
"Tcl_Alloc",
596644
"Tcl_ChannelName",
@@ -967,12 +1015,13 @@ fn validate_elf<Elf: FileHeader<Endian = Endianness>>(
9671015
allowed_libraries.push("libc.so".to_string());
9681016
}
9691017

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

9781027
let wanted_glibc_max_version = GLIBC_MAX_VERSION_BY_TRIPLE
@@ -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,15 @@ 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+
&& let Some((module, _)) = filename.to_string_lossy().split_once(".cpython-")
1284+
&& let Some(extra) = ALLOWED_DYLIBS_BY_MODULE.get(module)
1285+
{
1286+
allowed.extend(extra.clone());
1287+
}
12291288

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

0 commit comments

Comments
 (0)