Skip to content

Commit 2e23e69

Browse files
committed
[GR-75851] CompositeFileSystem symlink target mismatch.
1 parent ea39497 commit 2e23e69

2 files changed

Lines changed: 67 additions & 11 deletions

File tree

truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/TruffleFileTest.java

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1277,6 +1277,56 @@ protected Object execute(RootNode node, Env env, Object[] contextArguments, Obje
12771277
}
12781278
}
12791279

1280+
@Test
1281+
public void testCompositeFileSystemCreateSymbolicLinkRelativeTargetEscape() throws Exception {
1282+
Path tmp = Files.createTempDirectory("truffle-file-test");
1283+
try {
1284+
Path allowed = tmp.resolve("allowed").toAbsolutePath();
1285+
Files.createDirectories(allowed);
1286+
Path cwd = allowed.resolve("cwd").toAbsolutePath();
1287+
Files.createDirectories(cwd);
1288+
Path denied = tmp.resolve("denied").toAbsolutePath();
1289+
Files.writeString(denied, "denied");
1290+
1291+
FileSystem noIO = FileSystem.newDenyIOFileSystem();
1292+
FileSystem fullIO = FileSystem.newDefaultFileSystem();
1293+
FileSystem.Selector fullIOPredicate = new FileSystem.Selector(fullIO) {
1294+
@Override
1295+
public boolean test(Path path) {
1296+
return path.startsWith(allowed);
1297+
}
1298+
};
1299+
FileSystem compositeFs = FileSystem.newCompositeFileSystem(noIO, fullIOPredicate);
1300+
compositeFs.setCurrentWorkingDirectory(cwd);
1301+
1302+
IOAccess ioAccess = IOAccess.newBuilder().fileSystem(compositeFs).build();
1303+
try (Context context = Context.newBuilder().allowIO(ioAccess).build()) {
1304+
assertFails(
1305+
() -> AbstractExecutableTestLanguage.evalTestLanguage(
1306+
context, TestCompositeFileSystemCreateSymbolicLinkRelativeTargetEscape.class, "", allowed.toString()),
1307+
PolyglotException.class,
1308+
(e) -> Assert.assertTrue(e.getMessage().contains("Cross file system linking is not supported.")));
1309+
assertFalse(Files.exists(allowed.resolve("link")));
1310+
}
1311+
} finally {
1312+
delete(tmp);
1313+
}
1314+
}
1315+
1316+
@Registration
1317+
static final class TestCompositeFileSystemCreateSymbolicLinkRelativeTargetEscape extends AbstractExecutableTestLanguage {
1318+
1319+
@Override
1320+
@TruffleBoundary
1321+
protected Object execute(RootNode node, Env env, Object[] contextArguments, Object[] frameArguments) throws Exception {
1322+
TruffleFile allowed = env.getPublicTruffleFile((String) contextArguments[0]);
1323+
TruffleFile link = allowed.resolve("link");
1324+
TruffleFile target = env.getPublicTruffleFile("..").resolve("denied");
1325+
link.createSymbolicLink(target);
1326+
return null;
1327+
}
1328+
}
1329+
12801330
private static void delete(Path path) throws IOException {
12811331
if (Files.isDirectory(path)) {
12821332
try (DirectoryStream<Path> dir = Files.newDirectoryStream(path)) {

truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/FileSystems.java

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1725,8 +1725,11 @@ public void createLink(Path link, Path existing) throws IOException {
17251725
@Override
17261726
public void createSymbolicLink(Path link, Path target, FileAttribute<?>... attrs) throws IOException {
17271727
FileSystemInfo linkFileSystemInfo = selectFileSystem(link);
1728-
FileSystemInfo targetFileSystemInfo = selectFileSystem(target);
1729-
if (linkFileSystemInfo.fileSystem == targetFileSystemInfo.fileSystem) {
1728+
Path absoluteLink = linkFileSystemInfo.absolutePath;
1729+
Path linkParent = absoluteLink.getParent();
1730+
// A relative symbolic-link target is interpreted relative to the link's parent.
1731+
Path normalizedTarget = target.isAbsolute() ? target.normalize() : (linkParent == null ? absoluteLink : linkParent).resolve(target).normalize();
1732+
if (linkFileSystemInfo.fileSystem == findOwningFileSystem(normalizedTarget)) {
17301733
linkFileSystemInfo.fileSystem.createSymbolicLink(linkFileSystemInfo.path, target);
17311734
} else {
17321735
throw new IOException("Cross file system linking is not supported.");
@@ -1836,20 +1839,23 @@ private FileSystemInfo selectFileSystem(Path path) {
18361839
changeDirLock.readLock().lock();
18371840
try {
18381841
Path absolutePath = toNormalizedAbsolutePath(path);
1839-
FileSystem fs = fallBackFileSystem;
1840-
for (Selector delegate : delegates) {
1841-
if (delegate.test(absolutePath)) {
1842-
fs = delegate.getFileSystem();
1843-
break;
1844-
}
1845-
}
1846-
return new FileSystemInfo(fs, currentWorkingDirectory != null ? absolutePath : path);
1842+
FileSystem fs = findOwningFileSystem(absolutePath);
1843+
return new FileSystemInfo(fs, currentWorkingDirectory != null ? absolutePath : path, absolutePath);
18471844
} finally {
18481845
changeDirLock.readLock().unlock();
18491846
}
18501847
}
18511848

1852-
private record FileSystemInfo(FileSystem fileSystem, Path path) {
1849+
private FileSystem findOwningFileSystem(Path absolutePath) {
1850+
for (Selector delegate : delegates) {
1851+
if (delegate.test(absolutePath)) {
1852+
return delegate.getFileSystem();
1853+
}
1854+
}
1855+
return fallBackFileSystem;
1856+
}
1857+
1858+
private record FileSystemInfo(FileSystem fileSystem, Path path, Path absolutePath) {
18531859
}
18541860

18551861
private Path toNormalizedAbsolutePath(Path path) {

0 commit comments

Comments
 (0)