Skip to content

Commit d893ca1

Browse files
authored
Test file override outside of model directory (#6516)
* Add boost-filesystem
1 parent 53b2fac commit d893ca1

File tree

4 files changed

+90
-1
lines changed

4 files changed

+90
-1
lines changed

Dockerfile.win10.min

+1-1
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ RUN git clone --single-branch --depth=1 -b %VCPGK_VERSION% https://github.com/mi
9797
WORKDIR /vcpkg
9898
RUN bootstrap-vcpkg.bat
9999
RUN vcpkg.exe update
100-
RUN vcpkg.exe install openssl:x64-windows openssl-windows:x64-windows rapidjson:x64-windows re2:x64-windows boost-interprocess:x64-windows boost-stacktrace:x64-windows zlib:x64-windows pthread:x64-windows b64:x64-windows
100+
RUN vcpkg.exe install openssl:x64-windows openssl-windows:x64-windows rapidjson:x64-windows re2:x64-windows boost-filesystem:x64-windows boost-interprocess:x64-windows boost-stacktrace:x64-windows zlib:x64-windows pthread:x64-windows b64:x64-windows
101101
RUN vcpkg.exe integrate install
102102

103103
LABEL VCPGK_VERSION=${VCPGK_VERSION}

build.py

+9
Original file line numberDiff line numberDiff line change
@@ -1034,6 +1034,7 @@ def create_dockerfile_buildbase(ddir, dockerfile_name, argmap):
10341034
RUN wget -O /tmp/boost.tar.gz \
10351035
https://boostorg.jfrog.io/artifactory/main/release/1.80.0/source/boost_1_80_0.tar.gz && \
10361036
(cd /tmp && tar xzf boost.tar.gz) && \
1037+
cd /tmp/boost_1_80_0 && ./bootstrap.sh --prefix=/usr && ./b2 install && \
10371038
mv /tmp/boost_1_80_0/boost /usr/include/boost
10381039
10391040
# Server build requires recent version of CMake (FetchContent required)
@@ -1257,6 +1258,14 @@ def dockerfile_prepare_container_linux(argmap, backends, enable_gpu, target_mach
12571258
{backend_dependencies} && \
12581259
rm -rf /var/lib/apt/lists/*
12591260
1261+
# Install boost version >= 1.78 for boost::span
1262+
# Current libboost-dev apt packages are < 1.78, so install from tar.gz
1263+
RUN wget -O /tmp/boost.tar.gz \
1264+
https://boostorg.jfrog.io/artifactory/main/release/1.80.0/source/boost_1_80_0.tar.gz && \
1265+
(cd /tmp && tar xzf boost.tar.gz) && \
1266+
cd /tmp/boost_1_80_0 && ./bootstrap.sh --prefix=/usr && ./b2 install && \
1267+
rm -rf /tmp/boost*
1268+
12601269
# Set TCMALLOC_RELEASE_RATE for users setting LD_PRELOAD with tcmalloc
12611270
ENV TCMALLOC_RELEASE_RATE 200
12621271
""".format(

qa/L0_lifecycle/lifecycle_test.py

+78
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030

3131
sys.path.append("../common")
3232

33+
import base64
3334
import concurrent.futures
3435
import json
3536
import os
@@ -2554,6 +2555,83 @@ def test_file_override(self):
25542555
model_shape,
25552556
)
25562557

2558+
# Test that model load API file override can't be used to create files
2559+
# outside of any model directory.
2560+
def test_file_override_security(self):
2561+
# When using model load API, temporary model directories are created in
2562+
# a randomly generated /tmp/folderXXXXXX directory for the life of the
2563+
# model, and cleaned up on model unload.
2564+
model_basepath = "/tmp/folderXXXXXX"
2565+
if os.path.exists(model_basepath) and os.path.isdir(model_basepath):
2566+
shutil.rmtree(model_basepath)
2567+
os.makedirs(model_basepath)
2568+
2569+
# Set file override paths that try to escape out of model directory,
2570+
# and test both pre-existing and non-existent files.
2571+
root_home_dir = "/root"
2572+
2573+
# Relative paths
2574+
escape_dir_rel = os.path.join("..", "..", "root")
2575+
escape_dir_full = os.path.join(model_basepath, escape_dir_rel)
2576+
self.assertEqual(os.path.abspath(escape_dir_full), root_home_dir)
2577+
2578+
new_file_rel = os.path.join(escape_dir_rel, "new_dir", "test.txt")
2579+
self.assertFalse(os.path.exists(os.path.join(model_basepath, new_file_rel)))
2580+
existing_file_rel = os.path.join(escape_dir_rel, ".bashrc")
2581+
self.assertTrue(os.path.exists(os.path.join(model_basepath, existing_file_rel)))
2582+
2583+
# Symlinks
2584+
## No easy way to inject symlink into generated temp model dir, so for
2585+
## testing sake, make a fixed symlink path in /tmp.
2586+
escape_dir_symlink_rel = os.path.join("..", "escape_symlink")
2587+
escape_dir_symlink_full = "/tmp/escape_symlink"
2588+
self.assertEqual(
2589+
os.path.abspath(os.path.join(model_basepath, escape_dir_symlink_rel)),
2590+
escape_dir_symlink_full,
2591+
)
2592+
if os.path.exists(escape_dir_symlink_full):
2593+
os.unlink(escape_dir_symlink_full)
2594+
os.symlink(root_home_dir, escape_dir_symlink_full)
2595+
self.assertTrue(os.path.abspath(escape_dir_symlink_full), root_home_dir)
2596+
2597+
symlink_new_file_rel = os.path.join(
2598+
escape_dir_symlink_rel, "new_dir", "test.txt"
2599+
)
2600+
self.assertFalse(
2601+
os.path.exists(os.path.join(model_basepath, symlink_new_file_rel))
2602+
)
2603+
symlink_existing_file_rel = os.path.join(escape_dir_symlink_rel, ".bashrc")
2604+
self.assertTrue(
2605+
os.path.exists(os.path.join(model_basepath, symlink_existing_file_rel))
2606+
)
2607+
2608+
# Contents to try writing to file, though it should fail to be written
2609+
new_contents = "This shouldn't exist"
2610+
new_contents_b64 = base64.b64encode(new_contents.encode())
2611+
2612+
new_files = [new_file_rel, symlink_new_file_rel]
2613+
existing_files = [existing_file_rel, symlink_existing_file_rel]
2614+
all_files = new_files + existing_files
2615+
for filepath in all_files:
2616+
# minimal config to create a new model
2617+
config = json.dumps({"backend": "identity"})
2618+
files = {f"file:{filepath}": new_contents_b64}
2619+
with httpclient.InferenceServerClient("localhost:8000") as client:
2620+
with self.assertRaisesRegex(InferenceServerException, "failed to load"):
2621+
client.load_model("new_model", config=config, files=files)
2622+
2623+
for rel_path in new_files:
2624+
# Assert new file wasn't created
2625+
self.assertFalse(os.path.exists(os.path.join(model_basepath, rel_path)))
2626+
2627+
for rel_path in existing_files:
2628+
# Read the existing file and make sure it's contents weren't overwritten
2629+
existing_file = os.path.join(model_basepath, rel_path)
2630+
self.assertTrue(os.path.exists(existing_file))
2631+
with open(existing_file) as f:
2632+
contents = f.read()
2633+
self.assertNotEqual(contents, new_contents)
2634+
25572635
def test_shutdown_dynamic(self):
25582636
model_shape = (1, 1)
25592637
input_data = np.ones(shape=(1, 1), dtype=np.float32)

qa/L0_lifecycle/test.sh

+2
Original file line numberDiff line numberDiff line change
@@ -1533,6 +1533,8 @@ rm -f $CLIENT_LOG
15331533
set +e
15341534
python $LC_TEST LifeCycleTest.test_file_override >>$CLIENT_LOG 2>&1
15351535
check_unit_test
1536+
python $LC_TEST LifeCycleTest.test_file_override_security >>$CLIENT_LOG 2>&1
1537+
check_unit_test
15361538
set -e
15371539

15381540
kill $SERVER_PID

0 commit comments

Comments
 (0)