Skip to content

Commit 147e12f

Browse files
committed
[GR-42674] JS Fuzzing: InvalidPathException at UnixPath.checkNotNul(UnixPath.java:90)
PullRequest: graal/13298
2 parents 4fc1f92 + 68ce71e commit 147e12f

File tree

10 files changed

+86
-30
lines changed

10 files changed

+86
-30
lines changed

sdk/src/org.graalvm.polyglot/src/org/graalvm/polyglot/io/FileSystem.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,8 @@ public interface FileSystem {
8080
* @param uri the {@link URI} to be converted to {@link Path}
8181
* @return the {@link Path} representing given {@link URI}
8282
* @throws UnsupportedOperationException when {@link URI} scheme is not supported
83+
* @throws IllegalArgumentException if preconditions on the {@code uri} do not hold. The format
84+
* of the URI is {@link FileSystem} specific.
8385
* @since 19.0
8486
*/
8587
Path parsePath(URI uri);
@@ -91,6 +93,8 @@ public interface FileSystem {
9193
* @param path the string path to be converted to {@link Path}
9294
* @return the {@link Path}
9395
* @throws UnsupportedOperationException when the {@link FileSystem} supports only {@link URI}
96+
* @throws IllegalArgumentException if the {@code path} string cannot be converted to a
97+
* {@link Path}
9498
* @since 19.0
9599
*/
96100
Path parsePath(String path);

truffle/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ This changelog summarizes major changes between Truffle versions relevant to lan
1515
* GR-40274 TruffleStrings: added AsNativeNode and GetStringCompactionLevelNode.
1616
* GR-39189 Added attach methods on the `Instrumenter` class, that take `NearestSectionFilter` as a parameter. The new `NearestSectionFilter` class can be used to instrument or detect nearest locations to a given source line and column. For example, this can be used to implement breakpoints, where the exact line or column is not always precise and the location needs to be updated when new code is loaded.
1717
* GR-39189 Added `InstrumentableNode.findNearestNodeAt(int line, int column, ...)` to find the nearest node to the given source line and column. This is an alternative to the existing method that takes character offset.
18+
* GR-42674 It has been documented that methods `TruffleLanguage.Env#getPublicTruffleFile`, `TruffleLanguage.Env#getInternalTruffleFile`, `TruffleLanguage.Env#getTruffleFileInternal` and `TruffleInstrument.Env#getPublicTruffleFile` can throw `IllegalArgumentException` when the path string cannot be converted to a `Path` or uri preconditions required by the `FileSystem` do not hold.
1819

1920
## Version 22.3.0
2021

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ public Path parsePath(String path) {
157157
public Path parsePath(URI uri) {
158158
try {
159159
return Paths.get(uri);
160-
} catch (IllegalArgumentException | FileSystemNotFoundException e) {
160+
} catch (FileSystemNotFoundException e) {
161161
throw new UnsupportedOperationException(e);
162162
}
163163
}

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,8 @@ public void testParsePath() {
129129
expectException(() -> fs.parsePath((String) null), NullPointerException.class);
130130
expectException(() -> fs.parsePath((URI) null), NullPointerException.class);
131131
expectException(() -> fs.parsePath(new URI("unknownscheme:///tmp/")), UnsupportedOperationException.class);
132+
expectException(() -> fs.parsePath("\0"), IllegalArgumentException.class);
133+
expectException(() -> fs.parsePath(new URI("file://host:8000/tmp")), IllegalArgumentException.class);
132134
}
133135

134136
@Test

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

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -798,7 +798,7 @@ public void close() throws IOException {
798798
}
799799

800800
@Test
801-
public void testInvalidScheme() throws IOException {
801+
public void testInvalidURI() throws IOException {
802802
URI httpScheme = URI.create("http://127.0.0.1/foo.js");
803803
Path tmp = Files.createTempDirectory("invalidscheme");
804804
Path existingJar = tmp.resolve("exiting.jar");
@@ -810,17 +810,20 @@ public void testInvalidScheme() throws IOException {
810810
Path nonExistingJar = tmp.resolve("non_existing.jar");
811811
URI jarSchemeExistingFile = URI.create("jar:" + existingJar.toUri() + "!/");
812812
URI jarSchemeNonExistingFile = URI.create("jar:" + nonExistingJar.toUri() + "!/");
813+
URI invalidUri = URI.create("file://localhost:8000/tmp");
813814
java.nio.file.FileSystem nioJarFS = FileSystems.newFileSystem(jarSchemeExistingFile, Collections.emptyMap());
814815
try {
815816
// Context with enabled IO
816817
testInvalidSchemeImpl(httpScheme);
817818
testInvalidSchemeImpl(jarSchemeExistingFile);
818819
testInvalidSchemeImpl(jarSchemeNonExistingFile);
820+
testWrongURIPreconditionsImpl(invalidUri);
819821
// Context with disabled IO
820822
setupEnv(Context.newBuilder().build());
821823
testInvalidSchemeImpl(httpScheme);
822824
testInvalidSchemeImpl(jarSchemeExistingFile);
823825
testInvalidSchemeImpl(jarSchemeNonExistingFile);
826+
testWrongURIPreconditionsImpl(invalidUri);
824827
} finally {
825828
nioJarFS.close();
826829
delete(tmp);
@@ -833,6 +836,20 @@ private void testInvalidSchemeImpl(URI uri) {
833836
assertFails(() -> languageEnv.getTruffleFileInternal(uri, (f) -> false), UnsupportedOperationException.class);
834837
}
835838

839+
private void testWrongURIPreconditionsImpl(URI uri) {
840+
assertFails(() -> languageEnv.getPublicTruffleFile(uri), IllegalArgumentException.class);
841+
assertFails(() -> languageEnv.getInternalTruffleFile(uri), IllegalArgumentException.class);
842+
assertFails(() -> languageEnv.getTruffleFileInternal(uri, (f) -> false), IllegalArgumentException.class);
843+
}
844+
845+
@Test
846+
public void testInvalidPath() {
847+
String invalidPath = "\0";
848+
assertFails(() -> languageEnv.getPublicTruffleFile(invalidPath), IllegalArgumentException.class);
849+
assertFails(() -> languageEnv.getInternalTruffleFile(invalidPath), IllegalArgumentException.class);
850+
assertFails(() -> languageEnv.getTruffleFileInternal(invalidPath, (f) -> false), IllegalArgumentException.class);
851+
}
852+
836853
private static void delete(Path path) throws IOException {
837854
if (Files.isDirectory(path)) {
838855
try (DirectoryStream<Path> dir = Files.newDirectoryStream(path)) {

truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleLanguage.java

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2725,6 +2725,8 @@ public boolean isPreInitialization() {
27252725
* @return {@link TruffleFile}
27262726
* @throws UnsupportedOperationException when the {@link FileSystem} supports only
27272727
* {@link URI}
2728+
* @throws IllegalArgumentException if the {@code path} string cannot be converted to a
2729+
* {@link Path}
27282730
* @see IOAccess
27292731
* @see Builder#allowIO(IOAccess)
27302732
* @since 19.3.0
@@ -2735,7 +2737,7 @@ public TruffleFile getPublicTruffleFile(String path) {
27352737
FileSystemContext fs = getPublicFileSystemContext();
27362738
try {
27372739
return new TruffleFile(fs, fs.fileSystem.parsePath(path));
2738-
} catch (UnsupportedOperationException e) {
2740+
} catch (UnsupportedOperationException | IllegalArgumentException e) {
27392741
throw e;
27402742
} catch (Throwable t) {
27412743
throw TruffleFile.wrapHostException(t, fs.fileSystem);
@@ -2749,6 +2751,7 @@ public TruffleFile getPublicTruffleFile(String path) {
27492751
* @param uri the {@link URI} to create {@link TruffleFile} for
27502752
* @return {@link TruffleFile}
27512753
* @throws UnsupportedOperationException when {@link URI} scheme is not supported
2754+
* @throws IllegalArgumentException if preconditions on the {@code uri} do not hold.
27522755
* @since 19.3.0
27532756
*/
27542757
@TruffleBoundary
@@ -2757,7 +2760,7 @@ public TruffleFile getPublicTruffleFile(URI uri) {
27572760
FileSystemContext fs = getPublicFileSystemContext();
27582761
try {
27592762
return new TruffleFile(fs, fs.fileSystem.parsePath(uri));
2760-
} catch (UnsupportedOperationException e) {
2763+
} catch (UnsupportedOperationException | IllegalArgumentException e) {
27612764
throw e;
27622765
} catch (Throwable t) {
27632766
throw TruffleFile.wrapHostException(t, fs.fileSystem);
@@ -2783,6 +2786,8 @@ public TruffleFile getPublicTruffleFile(URI uri) {
27832786
* @since 19.3.0
27842787
* @throws UnsupportedOperationException when the {@link FileSystem} supports only
27852788
* {@link URI}
2789+
* @throws IllegalArgumentException if the {@code path} string cannot be converted to a
2790+
* {@link Path}
27862791
* @see #getTruffleFileInternal(String, Predicate)
27872792
* @see #getPublicTruffleFile(java.lang.String)
27882793
*/
@@ -2792,7 +2797,7 @@ public TruffleFile getInternalTruffleFile(String path) {
27922797
FileSystemContext fs = getInternalFileSystemContext();
27932798
try {
27942799
return new TruffleFile(fs, fs.fileSystem.parsePath(path));
2795-
} catch (UnsupportedOperationException e) {
2800+
} catch (UnsupportedOperationException | IllegalArgumentException e) {
27962801
throw e;
27972802
} catch (Throwable t) {
27982803
throw TruffleFile.wrapHostException(t, fs.fileSystem);
@@ -2807,6 +2812,7 @@ public TruffleFile getInternalTruffleFile(String path) {
28072812
* @return {@link TruffleFile}
28082813
* @since 19.3.0
28092814
* @throws UnsupportedOperationException when {@link URI} scheme is not supported
2815+
* @throws IllegalArgumentException if preconditions on the {@code uri} do not hold.
28102816
* @see #getTruffleFileInternal(URI, Predicate)
28112817
* @see #getPublicTruffleFile(java.net.URI)
28122818
*/
@@ -2816,7 +2822,7 @@ public TruffleFile getInternalTruffleFile(URI uri) {
28162822
FileSystemContext fs = getInternalFileSystemContext();
28172823
try {
28182824
return new TruffleFile(fs, fs.fileSystem.parsePath(uri));
2819-
} catch (UnsupportedOperationException e) {
2825+
} catch (UnsupportedOperationException | IllegalArgumentException e) {
28202826
throw e;
28212827
} catch (Throwable t) {
28222828
throw TruffleFile.wrapHostException(t, fs.fileSystem);
@@ -2861,6 +2867,8 @@ public TruffleFile getInternalTruffleFile(URI uri) {
28612867
* @return {@link TruffleFile}
28622868
* @throws UnsupportedOperationException when the {@link FileSystem} supports only
28632869
* {@link URI}
2870+
* @throws IllegalArgumentException if the {@code path} string cannot be converted to a
2871+
* {@link Path}
28642872
* @since 21.1.0
28652873
* @see #getTruffleFileInternal(URI, Predicate)
28662874
* @see #getPublicTruffleFile(String)
@@ -2883,6 +2891,7 @@ public TruffleFile getTruffleFileInternal(String path, Predicate<TruffleFile> fi
28832891
* @return {@link TruffleFile}
28842892
* @throws UnsupportedOperationException when the {@link FileSystem} supports only
28852893
* {@link URI}
2894+
* @throws IllegalArgumentException if preconditions on the {@code uri} do not hold.
28862895
* @since 21.1.0
28872896
* @see #getTruffleFileInternal(String, Predicate)
28882897
* @see #getPublicTruffleFile(URI)

truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/source/Source.java

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -993,8 +993,11 @@ static Source buildSource(String language, Object origin, String name, String pa
993993
if (useOrigin instanceof File) {
994994
final File file = (File) useOrigin;
995995
assert useFileSystemContext != null : "file system context must be provided by polyglot embedding API";
996-
TruffleFile truffleFile = SourceAccessor.getTruffleFile(file.toPath().toString(), useFileSystemContext);
997-
useOrigin = truffleFile;
996+
try {
997+
useOrigin = SourceAccessor.getTruffleFile(file.toPath().toString(), useFileSystemContext);
998+
} catch (UnsupportedOperationException | IllegalArgumentException e) {
999+
throw new AssertionError("Inconsistent path", e);
1000+
}
9981001
}
9991002

10001003
if (useOrigin == CONTENT_UNSET) {
@@ -1037,19 +1040,10 @@ static Source buildSource(String language, Object origin, String name, String pa
10371040
useUri = useUri == null ? tmpUri : useUri;
10381041
usePath = usePath == null ? useUrl.getPath() : usePath;
10391042
useFileSystemContext = useFileSystemContext == null ? SourceAccessor.ACCESSOR.engineSupport().getCurrentFileSystemContext() : useFileSystemContext;
1043+
assert useTruffleFile == null;
10401044
try {
10411045
useTruffleFile = SourceAccessor.getTruffleFile(tmpUri, useFileSystemContext);
1042-
useTruffleFile = getCanonicalFileIfItExists(useTruffleFile);
1043-
if (useContent == CONTENT_UNSET) {
1044-
if (isCharacterBased(useFileSystemContext, language, useMimeType)) {
1045-
String fileMimeType = useMimeType == null ? SourceAccessor.detectMimeType(useTruffleFile, getValidMimeTypes(useFileSystemContext, language)) : useMimeType;
1046-
useEncoding = useEncoding == null ? findEncoding(useTruffleFile, fileMimeType) : useEncoding;
1047-
useContent = read(useTruffleFile, useEncoding);
1048-
} else {
1049-
useContent = ByteSequence.create(useTruffleFile.readAllBytes());
1050-
}
1051-
}
1052-
} catch (UnsupportedOperationException uoe) {
1046+
} catch (IllegalArgumentException | UnsupportedOperationException e) {
10531047
if (ALLOW_IO && SourceAccessor.isSocketIOAllowed(useFileSystemContext)) {
10541048
// Not a recognized by FileSystem, fall back to URLConnection only for allowed
10551049
// IO without a custom FileSystem
@@ -1066,6 +1060,18 @@ static Source buildSource(String language, Object origin, String name, String pa
10661060
throw new SecurityException("Reading of URL " + useUrl + " is not allowed.");
10671061
}
10681062
}
1063+
if (useTruffleFile != null) {
1064+
useTruffleFile = getCanonicalFileIfItExists(useTruffleFile);
1065+
if (useContent == CONTENT_UNSET) {
1066+
if (isCharacterBased(useFileSystemContext, language, useMimeType)) {
1067+
String fileMimeType = useMimeType == null ? SourceAccessor.detectMimeType(useTruffleFile, getValidMimeTypes(useFileSystemContext, language)) : useMimeType;
1068+
useEncoding = useEncoding == null ? findEncoding(useTruffleFile, fileMimeType) : useEncoding;
1069+
useContent = read(useTruffleFile, useEncoding);
1070+
} else {
1071+
useContent = ByteSequence.create(useTruffleFile.readAllBytes());
1072+
}
1073+
}
1074+
}
10691075
} else if (useOrigin instanceof Reader) {
10701076
final Reader r = (Reader) useOrigin;
10711077
useContent = useContent == CONTENT_UNSET ? read(r) : useContent;

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

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -297,11 +297,7 @@ public boolean hasNoAccess() {
297297

298298
@Override
299299
public Path parsePath(URI path) {
300-
try {
301-
return wrap(delegate.parsePath(path));
302-
} catch (IllegalArgumentException | FileSystemNotFoundException e) {
303-
throw new UnsupportedOperationException(e);
304-
}
300+
return wrap(delegate.parsePath(path));
305301
}
306302

307303
@Override
@@ -771,17 +767,22 @@ public boolean hasNoAccess() {
771767

772768
@Override
773769
public Path parsePath(URI uri) {
770+
if (!hostfs.getScheme().equals(uri.getScheme())) {
771+
// Throw a UnsupportedOperationException with a better message than the default
772+
// FileSystemProvider.getPath does.
773+
throw new UnsupportedOperationException("Unsupported URI scheme " + uri.getScheme());
774+
}
774775
try {
775776
return hostfs.getPath(uri);
776-
} catch (IllegalArgumentException | FileSystemNotFoundException e) {
777+
} catch (FileSystemNotFoundException e) {
777778
throw new UnsupportedOperationException(e);
778779
}
779780
}
780781

781782
@Override
782783
public Path parsePath(String path) {
783784
if (!"file".equals(hostfs.getScheme())) {
784-
throw new IllegalStateException("The ParsePath(String path) should be called only for file scheme.");
785+
throw new UnsupportedOperationException("The ParsePath(String path) should be called only for file scheme.");
785786
}
786787
return Paths.get(path);
787788
}
@@ -1027,7 +1028,7 @@ public Path parsePath(final URI uri) {
10271028
// Paths.get(URI) cannot be used as it looks up the file system provider
10281029
// by scheme and can use a non default file system provider.
10291030
return defaultFileSystemProvider.getPath(uri);
1030-
} catch (IllegalArgumentException | FileSystemNotFoundException e) {
1031+
} catch (FileSystemNotFoundException e) {
10311032
throw new UnsupportedOperationException(e);
10321033
}
10331034
}

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

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
import java.io.StringWriter;
5353
import java.lang.ref.Reference;
5454
import java.lang.ref.ReferenceQueue;
55+
import java.nio.file.Path;
5556
import java.time.Duration;
5657
import java.time.ZoneId;
5758
import java.util.ArrayList;
@@ -1729,8 +1730,18 @@ public PolyglotContextImpl createContext(OutputStream configOut, OutputStream co
17291730
fileSystemConfig = new FileSystemConfig(ioAccess, FileSystems.newNoIOFileSystem(), FileSystems.newLanguageHomeFileSystem());
17301731
}
17311732
if (currentWorkingDirectory != null) {
1732-
fileSystemConfig.fileSystem.setCurrentWorkingDirectory(fileSystemConfig.fileSystem.parsePath(currentWorkingDirectory));
1733-
fileSystemConfig.internalFileSystem.setCurrentWorkingDirectory(fileSystemConfig.internalFileSystem.parsePath(currentWorkingDirectory));
1733+
Path publicFsCwd;
1734+
Path internalFsCwd;
1735+
try {
1736+
publicFsCwd = fileSystemConfig.fileSystem.parsePath(currentWorkingDirectory);
1737+
internalFsCwd = fileSystemConfig.internalFileSystem.parsePath(currentWorkingDirectory);
1738+
} catch (IllegalArgumentException e) {
1739+
throw PolyglotEngineException.illegalArgument(e);
1740+
} catch (UnsupportedOperationException e) {
1741+
throw PolyglotEngineException.illegalArgument(new IllegalArgumentException(e));
1742+
}
1743+
fileSystemConfig.fileSystem.setCurrentWorkingDirectory(publicFsCwd);
1744+
fileSystemConfig.internalFileSystem.setCurrentWorkingDirectory(internalFsCwd);
17341745
}
17351746
final OutputStream useOut;
17361747
if (configOut == null || configOut == INSTRUMENT.getOut(this.out)) {

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -528,7 +528,12 @@ public String findLanguage(URL url) throws IOException {
528528
@Override
529529
public String findMimeType(File file) throws IOException {
530530
Objects.requireNonNull(file);
531-
TruffleFile truffleFile = EngineAccessor.LANGUAGE.getTruffleFile(file.toPath().toString(), getDefaultFileSystemContext());
531+
TruffleFile truffleFile;
532+
try {
533+
truffleFile = EngineAccessor.LANGUAGE.getTruffleFile(file.toPath().toString(), getDefaultFileSystemContext());
534+
} catch (UnsupportedOperationException | IllegalArgumentException e) {
535+
throw new AssertionError("Inconsistent path", e);
536+
}
532537
return truffleFile.detectMimeType();
533538
}
534539

0 commit comments

Comments
 (0)