Skip to content

Commit eef020f

Browse files
committed
FIX: PR #744 review — arch-correctness gates, osx-arm64 honesty, and bloat cleanup
Correctness / arch: - audit_bundled_binaries.py: add an ELF e_machine arch gate (linux-64==x86_64, linux-aarch64==aarch64) -- the Linux twin of assert_pe_machine.py; +3 tests. - consolidate-conda-artifacts-job.yml: reconcile the win-arm64 accounting (25->28). - build-conda-packages.sh + conda-build-pipeline.yml: correct the false osx-arm64 "static arm64-slice audit (lipo/otool)" claims (no such check exists) to admit the arm64 slice is trusted from the universal2 wheel tag; real guards are PE + ELF. Hygiene: - rename test_026_driver_load_probe.py -> test_033 (dup with main's test_026). - build-conda-packages.sh: idempotent conda_builder env create (set -e safe). - tls_connect_probe.py: MS-ODBCSTR }} brace escaping in _split_top_level. Bloat removal (ponytail): - drop the YAGNI _DRIVER_LOAD_FAILURE_MARKERS / _OPENSSL_UNREACHABLE_MARKERS tables (describe() decoration only; classifiers use separate positive-marker lists). - remove the dead targetArch param + always-true ne(...,'arm64') condition + args. - remove the vestigial -Package/-DriverCondaDir params + odbc no-op branches from build-conda-packages.ps1. Probe/audit unit tests pass; black clean. Validated on a NonOfficial ADO build run.
1 parent 60244a1 commit eef020f

10 files changed

Lines changed: 201 additions & 195 deletions

‎OneBranchPipelines/conda-build-pipeline.yml‎

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,6 @@ extends:
150150
parameters:
151151
pythonVersion: '${{ parameters.pythonVersions }}'
152152
condaSubdir: 'win-64'
153-
targetArch: 'x64'
154153
odbcWheelDir: '$(Pipeline.Workspace)/odbc_wheels'
155154
odbcWheelFilter: 'mssql_python_odbc-*win_amd64.whl'
156155
# win-arm64 conda build: CROSS-built on THIS x64 agent via CONDA_SUBDIR, in the
@@ -169,7 +168,6 @@ extends:
169168
pythonVersion: '3.12,3.13,3.14'
170169
condaSubdir: 'win-arm64'
171170
condaTargetSubdir: 'win-arm64'
172-
targetArch: 'x64'
173171
odbcWheelDir: '$(Pipeline.Workspace)/odbc_wheels'
174172
odbcWheelFilter: 'mssql_python_odbc-*win_arm64.whl'
175173
outputDir: '$(Agent.TempDirectory)/conda-bld-winarm64'
@@ -222,7 +220,8 @@ extends:
222220
artifactName: 'drop_ConsolidateOdbc_ConsolidateArtifacts'
223221
targetPath: '$(Pipeline.Workspace)/odbc_wheels'
224222
# osx-arm64: CROSS-built on the Intel agent (BEST-EFFORT -- the runtime import
225-
# auto-skips; the static arm64-slice audit stands in).
223+
# auto-skips). NOTE: osx-arm64 arch is NOT independently verified here (no
224+
# Mach-O arch check); it is trusted from the universal2 wheel tag, like PyPI.
226225
- template: /OneBranchPipelines/steps/conda-build-validate-step-posix.yml@self
227226
parameters:
228227
condaSubdir: 'osx-arm64'

‎OneBranchPipelines/jobs/consolidate-conda-artifacts-job.yml‎

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,19 @@
1313
# package (which vendors the ODBC payload) is emitted per-Python by each build leg;
1414
# there is NO separate companion package:
1515
# win-64 : 5 py x mssql-python = 5
16+
# win-arm64 : 3 py x mssql-python (py3.12-3.14, cross-built on x64) = 3
1617
# osx-64 : 5 py x mssql-python (Intel Mac, cross-built via Rosetta) = 5
1718
# osx-arm64 : 5 py x mssql-python (Apple Silicon, native) = 5
1819
# linux-64 : 5 py x mssql-python (glibc x86_64 host, native) = 5
1920
# linux-aarch64 : 5 py x mssql-python (x86_64 host + QEMU, best-effort) = 5
2021
# ------------------------------------------------------------------------------
21-
# TOTAL (PyPI parity minus win-arm64 + musllinux) = 25
22-
# win-arm64 (no import-validation host on x64) and musllinux (no conda musl subdir)
23-
# are intentionally NOT conda-built. This job is BEST-EFFORT and never hard-fails on
24-
# a short set; the release pipeline's conda-release-step enforces the hard gate
25-
# (required subdirs present + complete Python matrix) before anything is published.
22+
# TOTAL (PyPI parity minus musllinux) = 28
23+
# win-arm64 is cross-built for py3.12-3.14 only (py3.10/3.11 have no win-arm64 deps
24+
# on Anaconda defaults); its runtime import is skipped on the x64 host, so its arch
25+
# is enforced by the PE-machine assert (assert_pe_machine.py). musllinux (no conda
26+
# musl subdir) is intentionally NOT conda-built. This job is BEST-EFFORT and never
27+
# hard-fails on a short set; the release pipeline's conda-release-step enforces the
28+
# hard gate (required subdirs present + complete Python matrix) before publish.
2629
parameters:
2730
- name: oneBranchType
2831
type: string

‎OneBranchPipelines/scripts/build-conda-packages.ps1‎

Lines changed: 88 additions & 115 deletions
Large diffs are not rendered by default.

‎OneBranchPipelines/scripts/build-conda-packages.sh‎

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@
1111
# binfmt for linux-aarch64, and as a CROSS-build for osx-arm64 on the Intel macOS
1212
# agent (there is no reverse Rosetta, so the arm64 Python is never executed --
1313
# conda/*/build.sh extract the universal2 wheel without Python and the section-7
14-
# runtime import is skipped; the pipeline's static arm64-slice audit stands in).
14+
# runtime import is skipped -- osx-arm64 arch is NOT independently verified here
15+
# (trusted from the universal2 wheel tag, like the PyPI wheel; no Mach-O arch check)).
1516
#
1617
# Args:
1718
# $1 WheelsDir find-links dir holding the mssql-python + mssql-python-odbc wheels
@@ -27,7 +28,9 @@
2728
# target's Python -- true natively, under Rosetta 2 (osx-64
2829
# on Apple Silicon) and under QEMU binfmt (linux-aarch64 on
2930
# x86_64). For osx-arm64 on the Intel agent it is NOT, so
30-
# that leg auto-skips the import (static arch audit stands in).
31+
# that leg auto-skips the import. NOTE: osx-arm64 arch is not
32+
# independently verified here -- trusted from the universal2
33+
# wheel tag; there is no Mach-O arch check in this pipeline.
3134
set -euo pipefail
3235

3336
WheelsDir="${1:?WheelsDir required}"
@@ -139,6 +142,10 @@ echo "Using conda: $conda"
139142
# rides along so the RUNPATH audit reads .conda metadata from this same env.
140143
condaBuildEnv="conda_builder"
141144
echo "=== creating dedicated conda-build env ($condaBuildEnv: conda-build<26) ==="
145+
# Idempotent: a reused agent/workdir may already have this env, and a pre-existing
146+
# env makes `conda create` fail under `set -e`. Remove it first (best-effort, like
147+
# the verify envs below) so a rerun recreates cleanly.
148+
"$conda" env remove -y -n "$condaBuildEnv" 2>/dev/null || true
142149
"$conda" create -y -n "$condaBuildEnv" -c conda-forge --override-channels "conda-build<26" zstandard
143150

144151
# ---------------------------------------------------------------------------
@@ -290,14 +297,16 @@ for py in $pyvers; do
290297
"$conda" run -n "$envName" python -c "import sys" >/dev/null 2>&1 || target_runnable=0
291298
if [ "$target_runnable" = "0" ]; then
292299
# The ONLY leg allowed to skip the runtime proof is the osx-arm64 cross-build on
293-
# an Intel agent (no reverse Rosetta): the arm64 Python genuinely cannot run here,
294-
# and the pipeline's static arm64-slice audit (lipo/otool/file on the arm64 Mach-O
295-
# payload) stands in -- the same assurance as the shipping PyPI universal2 arm64
296-
# slice. Every OTHER target (linux-64/osx-64 native, linux-aarch64 under QEMU
297-
# binfmt) MUST run its own import; a leg that cannot is a real failure, never a
298-
# silent pass -- otherwise a broken linux-aarch64 package ships unvalidated.
300+
# an Intel agent (no reverse Rosetta): the arm64 Python genuinely cannot run here.
301+
# CAVEAT: osx-arm64 arch is NOT independently verified in this pipeline. Unlike
302+
# Windows (assert_pe_machine.py PE check) and Linux (audit_bundled_binaries.py ELF
303+
# e_machine check), there is NO Mach-O arch audit, so the arm64 slice is trusted
304+
# from the universal2 wheel tag -- exactly like the shipping PyPI universal2 wheel.
305+
# Every OTHER target (linux-64/osx-64 native, linux-aarch64 under QEMU binfmt) MUST
306+
# run its own import; a leg that cannot is a real failure, never a silent pass --
307+
# otherwise a broken linux-aarch64 package ships unvalidated.
299308
if [ "${CONDA_SUBDIR:-}" = "osx-arm64" ] && [ "$(uname -s)" = "Darwin" ]; then
300-
echo "=== [py $py] osx-arm64 cross on Intel: target Python not executable; skipping runtime import (static arm64-slice audit covers this leg). ==="
309+
echo "=== [py $py] osx-arm64 cross on Intel: target Python not executable; skipping runtime import (osx-arm64 arch NOT independently verified -- trusted from the universal2 wheel tag). ==="
301310
continue
302311
fi
303312
echo "ERROR: [py $py] target Python for CONDA_SUBDIR=${CONDA_SUBDIR:-native} is not executable on $(uname -s)/$(uname -m), and this is NOT the osx-arm64 cross-build. Refusing to silently skip validation (linux-aarch64 requires QEMU binfmt to be registered on this leg)." >&2

‎OneBranchPipelines/steps/conda-build-validate-step.yml‎

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,6 @@ parameters:
4040
- name: condaTargetSubdir
4141
type: string
4242
default: ''
43-
# Target architecture of the wheel build; the step is skipped unless 'x64'
44-
# (or a native arch) so cross-compiled legs don't attempt a conda build.
45-
- name: targetArch
46-
type: string
47-
default: 'x64'
4843
# Directory holding the freshly built mssql-python wheel (setup.py bdist_wheel).
4944
- name: mssqlWheelDir
5045
type: string
@@ -147,5 +142,4 @@ steps:
147142
Write-Host " staged $subdir/$($p.Name)"
148143
}
149144
displayName: 'Conda build + validate (${{ parameters.condaSubdir }} py${{ parameters.pythonVersion }})'
150-
condition: ne('${{ parameters.targetArch }}', 'arm64')
151145
continueOnError: ${{ parameters.continueOnError }}

‎conda/driver_load_probe.py‎

Lines changed: 1 addition & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -61,24 +61,6 @@
6161
"certificate",
6262
)
6363

64-
# Negative signals: the native driver did NOT load / link / resolve. Listed only
65-
# to produce a clearer FAIL message -- classification is allowlist-based, so an
66-
# unrecognized exception still fails closed even if it matches nothing here.
67-
_DRIVER_LOAD_FAILURE_MARKERS = (
68-
"failed to load the driver",
69-
"failed to load library",
70-
"failed to load required function pointers",
71-
"odbc driver not found",
72-
"mssql-auth.dll",
73-
"mssql-python-odbc",
74-
"cannot open shared object", # linux dlopen failure
75-
"image not found", # macOS dlopen failure
76-
"no such file or directory", # driver binary absent
77-
"can't open lib", # unixODBC could not open the driver
78-
"unsupported architecture",
79-
"unsupported platform",
80-
)
81-
8264

8365
def driver_loaded(exc):
8466
"""FAIL-CLOSED classifier for the connect outcome.
@@ -99,12 +81,7 @@ def describe(exc):
9981
"""Short, human-readable reason string for the probe's stdout / exit line."""
10082
if exc is None:
10183
return "clean connect"
102-
msg = str(exc)
103-
low = msg.lower()
104-
for marker in _DRIVER_LOAD_FAILURE_MARKERS:
105-
if marker in low:
106-
return "driver load failure -> " + msg[:300]
107-
return msg[:300]
84+
return str(exc)[:300]
10885

10986

11087
def main():

‎conda/tls_connect_probe.py‎

Lines changed: 16 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,9 @@
2020
These are the only PASS outcomes (see ``_TLS_COMPLETED_MARKERS``).
2121
* Every other outcome fails closed (non-zero exit). In particular an OpenSSL that
2222
could not be loaded surfaces BEFORE login as an ``SSL Provider`` /
23-
``libssl``/``libcrypto`` / ``cannot open shared object`` error -- classified
24-
here as ``OPENSSL BACKEND UNREACHABLE`` (see ``_OPENSSL_UNREACHABLE_MARKERS``),
25-
which is exactly the conda RUNPATH bug this gate is meant to catch.
23+
``libssl``/``libcrypto`` / ``cannot open shared object`` error -- it is not in
24+
``_TLS_COMPLETED_MARKERS`` so it fails closed, which is exactly the conda RUNPATH
25+
bug this gate is meant to catch.
2626
2727
IMPORTANT -- masking caveat: this gate is only CONCLUSIVE on a minimal base with
2828
NO system OpenSSL on the default loader path. On a full agent (or any host with a
@@ -59,22 +59,6 @@
5959
"password did not match",
6060
)
6161

62-
# Markers that mean the crypto backend could NOT be loaded / the handshake never
63-
# ran. Listed for a crisp FAIL message -- classification is allowlist-based, so an
64-
# unrecognized outcome fails closed even if it matches nothing here.
65-
_OPENSSL_UNREACHABLE_MARKERS = (
66-
"libssl",
67-
"libcrypto",
68-
"cannot open shared object", # linux dlopen failure of the crypto backend
69-
"image not found", # macOS dlopen failure
70-
"openssl",
71-
"ssl provider", # an SSL Provider error before login = crypto/handshake fail
72-
"ssl routines",
73-
"encryption not supported",
74-
"unable to load",
75-
"cannot load",
76-
)
77-
7862

7963
def tls_completed(exc):
8064
"""FAIL-CLOSED classifier: True only when the TLS handshake provably completed.
@@ -101,36 +85,40 @@ def describe(exc):
10185
"""Short, human-readable reason string for the gate's stdout / exit line."""
10286
if exc is None:
10387
return "clean connect (TLS handshake completed)"
104-
msg = str(exc)
105-
low = msg.lower()
106-
for marker in _OPENSSL_UNREACHABLE_MARKERS:
107-
if marker in low:
108-
return "OpenSSL backend unreachable -> " + msg[:300]
109-
return msg[:300]
88+
return str(exc)[:300]
11089

11190

11291
def _split_top_level(conn):
11392
"""Split an ODBC connection string on TOP-LEVEL ``;`` only.
11493
11594
An ODBC value wrapped in ``{...}`` may itself contain ``;`` (MS-ODBCSTR), so a
11695
naive ``split(';')`` would shred braced values. Track brace depth and break only
117-
at depth 0.
96+
at depth 0. Inside a braced value ``}}`` is an escaped literal ``}`` (MS-ODBCSTR),
97+
NOT a close -- consume both and keep the depth so the value is not split early.
11898
"""
11999
segments = []
120100
buf = ""
121101
depth = 0
122-
for ch in conn.strip():
102+
s = conn.strip()
103+
i = 0
104+
while i < len(s):
105+
ch = s[i]
123106
if ch == "{":
124107
depth += 1
125108
buf += ch
126109
elif ch == "}":
110+
if depth > 0 and i + 1 < len(s) and s[i + 1] == "}":
111+
buf += "}}" # escaped literal '}' inside a braced value; not a close
112+
i += 2
113+
continue
127114
depth = max(0, depth - 1)
128115
buf += ch
129116
elif ch == ";" and depth == 0:
130117
segments.append(buf)
131118
buf = ""
132119
else:
133120
buf += ch
121+
i += 1
134122
segments.append(buf)
135123
return segments
136124

@@ -279,7 +267,7 @@ def main():
279267
if tls_completed(outcome):
280268
print("TLS_OK (OpenSSL backend reachable; " + describe(outcome) + ")")
281269
return
282-
sys.exit("TLS/OPENSSL BACKEND UNREACHABLE: " + describe(outcome))
270+
sys.exit("TLS HANDSHAKE DID NOT COMPLETE: " + describe(outcome))
283271

284272

285273
if __name__ == "__main__":

‎eng/scripts/audit_bundled_binaries.py‎

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99
out of each built ``.conda`` payload -- via the ``PT_DYNAMIC`` program header the
1010
*loader itself* uses -- and asserts, statically and exactly:
1111
12+
* each driver/manager ELF's ``e_machine`` MATCHES the package's conda subdir
13+
(``linux-64`` == x86_64, ``linux-aarch64`` == aarch64), so a wrong-arch or
14+
mislabeled ``.so`` is caught statically (the Linux twin of ``assert_pe_machine.py``);
1215
* ``libmsodbcsql*`` and ``libodbcinst.so.2`` carry the EXACT relative ``$ORIGIN``
1316
climb that lands on the package-root ``lib`` (== ``$PREFIX/lib``), computed from
1417
each binary's own location -- not a substring, not "any ``..``". A too-short,
@@ -72,6 +75,15 @@
7275
_DRIVER_NEEDED = ("libkrb5", "libgssapi_krb5", "libodbcinst")
7376
_ODBCINST_NEEDED = ("libltdl",)
7477

78+
# ELF e_machine architecture ids (ELF header offset 0x12). The conda subdir is the
79+
# authority: every vendored driver/manager ELF must match it, so an x86_64 .so
80+
# mislabeled under a linux-aarch64 package is caught statically (the emulated aarch64
81+
# leg's runtime probe is best-effort and would not). Linux twin of assert_pe_machine.py.
82+
_EM_X86_64 = 62
83+
_EM_AARCH64 = 183
84+
_SUBDIR_MACHINE = {"linux-64": _EM_X86_64, "linux-aarch64": _EM_AARCH64}
85+
_MACHINE_NAME = {_EM_X86_64: "x86_64", _EM_AARCH64: "aarch64"}
86+
7587

7688
def _zstd_decompress(raw: bytes) -> bytes:
7789
"""Decompress a zstandard blob, preferring the 3.14+ stdlib backend."""
@@ -90,6 +102,17 @@ def _is_elf(data: bytes) -> bool:
90102
return len(data) >= 64 and data[:4] == b"\x7fELF"
91103

92104

105+
def elf_machine(data: bytes):
106+
"""Return the ELF ``e_machine`` architecture id (header offset 0x12), or None.
107+
108+
Endianness comes from ``e_ident[5]`` (the shipped drivers are ELF64-LE).
109+
"""
110+
if not _is_elf(data):
111+
return None
112+
en = "<" if data[5] == 1 else ">"
113+
return struct.unpack_from(en + "H", data, 0x12)[0]
114+
115+
93116
def elf_dynamic(data: bytes) -> dict:
94117
"""Return ``{'runpath': str|None, 'rpath': str|None, 'needed': [str]}``.
95118
@@ -289,6 +312,9 @@ def audit_package(path: str) -> list[str]:
289312
print(f" SKIP (no Linux ELF payload): {base_name} [subdir={subdir or '?'}]")
290313
return []
291314

315+
# The conda subdir is the arch authority; every vendored ELF must match it.
316+
expected_machine = _SUBDIR_MACHINE.get(subdir)
317+
292318
errors: list[str] = []
293319

294320
# N2a: the run deps that SERVICE the driver's krb5/openssl/libltdl must be declared.
@@ -342,6 +368,18 @@ def audit_package(path: str) -> list[str]:
342368
errors.append(f"{name}: expected an ELF binary but the header is not ELF.")
343369
continue
344370

371+
# Architecture gate: the ELF machine MUST match the package's conda subdir, so
372+
# an x86_64 driver mislabeled under a linux-aarch64 package (which the emulated
373+
# leg's best-effort runtime probe would not catch) fails here.
374+
if expected_machine is not None:
375+
mach = elf_machine(data)
376+
if mach != expected_machine:
377+
errors.append(
378+
f"{name}: ELF machine {mach} ({_MACHINE_NAME.get(mach, 'unknown')}) does "
379+
f"not match the '{subdir}' package arch {expected_machine} "
380+
f"({_MACHINE_NAME[expected_machine]}) -- wrong-arch/mislabeled driver."
381+
)
382+
345383
dyn = elf_dynamic(data)
346384
entries = _entries(effective_runpath(dyn))
347385
needed = dyn["needed"]
@@ -480,9 +518,9 @@ def main(argv: list | None = None) -> int:
480518
print("\nOK: no Linux packages present; nothing to audit (win/osx have no ELF payload).")
481519
else:
482520
print(
483-
f"\nOK: all {linux_checked} Linux package(s) carry the EXACT $ORIGIN climb, keep "
484-
f"their krb5/gssapi/libltdl NEEDEDs, declare krb5/libtool/openssl, and vendor no "
485-
f"crypto (conda services them)."
521+
f"\nOK: all {linux_checked} Linux package(s) match their subdir arch, carry the "
522+
f"EXACT $ORIGIN climb, keep their krb5/gssapi/libltdl NEEDEDs, declare "
523+
f"krb5/libtool/openssl, and vendor no crypto (conda services them)."
486524
)
487525
return 0
488526

0 commit comments

Comments
 (0)