Skip to content

Commit 28cf087

Browse files
committed
Avoid direct dependency on libcrypt
1 parent e26fa1e commit 28cf087

File tree

4 files changed

+67
-31
lines changed

4 files changed

+67
-31
lines changed

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/ErrorMessages.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1281,6 +1281,8 @@ public abstract class ErrorMessages {
12811281
public static final TruffleString INVALID_PARTIAL_STATE = tsLiteral("invalid partial state");
12821282
public static final TruffleString LOST_S = tsLiteral("lost %s");
12831283
public static final TruffleString CTYPES_FUNCTION_CALL_COULD_NOT_OBTAIN_FUNCTION_POINTER = tsLiteral("ctypes function call could not obtain function pointer");
1284+
public static final TruffleString UNABLE_TO_LOAD_LIBCRYPT = tsLiteral(
1285+
"Unable to load libcrypt library. Please install libxcrypt-compat (RPM-based distributions) package or libcrypt1 (DEB-based distributions).");
12841286

12851287
// ssl error messages
12861288
public static final TruffleString SSL_ERR_DECODING_PEM_FILE_S = tsLiteral("Error decoding PEM-encoded file: %s");

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/NFIPosixSupport.java

Lines changed: 63 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* The Universal Permissive License (UPL), Version 1.0
@@ -41,6 +41,7 @@
4141
// skip GIL
4242
package com.oracle.graal.python.runtime;
4343

44+
import static com.oracle.graal.python.nodes.StringLiterals.J_DEFAULT;
4445
import static com.oracle.graal.python.nodes.StringLiterals.J_NATIVE;
4546
import static com.oracle.graal.python.nodes.StringLiterals.J_NFI_LANGUAGE;
4647
import static com.oracle.graal.python.nodes.StringLiterals.T_LLVM_LANGUAGE;
@@ -91,8 +92,11 @@
9192
import org.graalvm.nativeimage.ImageInfo;
9293

9394
import com.oracle.graal.python.PythonLanguage;
95+
import com.oracle.graal.python.builtins.PythonBuiltinClassType;
9496
import com.oracle.graal.python.builtins.PythonOS;
9597
import com.oracle.graal.python.builtins.objects.exception.OSErrorEnum;
98+
import com.oracle.graal.python.nodes.ErrorMessages;
99+
import com.oracle.graal.python.nodes.PRaiseNode;
96100
import com.oracle.graal.python.runtime.PosixSupportLibrary.AcceptResult;
97101
import com.oracle.graal.python.runtime.PosixSupportLibrary.AddrInfoCursor;
98102
import com.oracle.graal.python.runtime.PosixSupportLibrary.AddrInfoCursorLibrary;
@@ -297,7 +301,7 @@ private enum PosixNativeFunction {
297301
call_sem_trywait("(pointer):sint32"),
298302
call_sem_timedwait("(pointer, sint64):sint32"),
299303

300-
call_crypt("([sint8], [sint8], [sint32]):sint64");
304+
crypt("([sint8], [sint8]):sint64");
301305

302306
private final String signature;
303307

@@ -377,20 +381,8 @@ private static String getLibPath(PythonContext context) {
377381
@TruffleBoundary
378382
private static void loadLibrary(NFIPosixSupport posix) {
379383
String path = getLibPath(posix.context);
380-
String backend = posix.nfiBackend.toJavaStringUncached();
381-
Env env = posix.context.getEnv();
382-
383-
posix.context.ensureNFILanguage(null, "PosixModuleBackend", "native");
384-
385-
String withClause = backend.equals(J_NATIVE) ? "" : "with " + backend;
386-
String src = String.format("%sload (RTLD_LOCAL) \"%s\"", withClause, path);
387-
Source loadSrc = Source.newBuilder(J_NFI_LANGUAGE, src, "load:" + SUPPORTING_NATIVE_LIB_NAME).internal(true).build();
388-
389-
if (LOGGER.isLoggable(Level.FINE)) {
390-
LOGGER.fine(String.format("Loading native library: %s", src));
391-
}
392384
try {
393-
posix.nfiLibrary = env.parseInternal(loadSrc).call();
385+
posix.nfiLibrary = loadLibrary(posix, path);
394386
} catch (Throwable e) {
395387
throw new UnsupportedOperationException(String.format("""
396388
Could not load posix support library from path '%s'. Troubleshooting:\s
@@ -400,6 +392,28 @@ private static void loadLibrary(NFIPosixSupport posix) {
400392
}
401393
}
402394

395+
@TruffleBoundary
396+
private static Object loadLibrary(NFIPosixSupport posix, String path) {
397+
String backend = posix.nfiBackend.toJavaStringUncached();
398+
Env env = posix.context.getEnv();
399+
400+
posix.context.ensureNFILanguage(null, "PosixModuleBackend", "native");
401+
402+
Source loadSrc;
403+
if (path != null) {
404+
String withClause = backend.equals(J_NATIVE) ? "" : "with " + backend;
405+
String src = String.format("%sload (RTLD_LOCAL) \"%s\"", withClause, path);
406+
if (LOGGER.isLoggable(Level.FINE)) {
407+
LOGGER.fine(String.format("Loading native library: %s", src));
408+
}
409+
loadSrc = Source.newBuilder(J_NFI_LANGUAGE, src, "load:" + SUPPORTING_NATIVE_LIB_NAME).internal(true).build();
410+
} else {
411+
loadSrc = Source.newBuilder(J_NFI_LANGUAGE, J_DEFAULT, J_DEFAULT).internal(true).build();
412+
}
413+
414+
return env.parseInternal(loadSrc).call();
415+
}
416+
403417
@TruffleBoundary
404418
private static void loadFunction(NFIPosixSupport posix, Object library, PosixNativeFunction function) {
405419
Object unbound;
@@ -430,6 +444,7 @@ public InteropLibrary getResultInterop() {
430444
private final PythonContext context;
431445
private final TruffleString nfiBackend;
432446
private volatile Object nfiLibrary;
447+
private volatile Object cryptLibrary;
433448
private final AtomicReferenceArray<Object> cachedFunctions;
434449
@CompilationFinal(dimensions = 1) private long[] constantValues;
435450

@@ -1848,7 +1863,24 @@ public TruffleString crypt(TruffleString word, TruffleString salt,
18481863
@Shared("tsCopyBytes") @Cached TruffleString.CopyToByteArrayNode copyToByteArrayNode,
18491864
@Shared("tsFromBytes") @Cached TruffleString.FromByteArrayNode fromByteArrayNode,
18501865
@Shared("fromUtf8") @Cached TruffleString.SwitchEncodingNode switchEncodingFromUtf8Node) throws PosixException {
1851-
int[] lenArray = new int[1];
1866+
/*
1867+
* We don't want to link the posix library with libcrypt, because it might not be available
1868+
* on the target system and would make the whole posix library fail to load. So we load it
1869+
* dynamically on demand.
1870+
*/
1871+
if (injectBranchProbability(SLOWPATH_PROBABILITY, cryptLibrary == null)) {
1872+
try {
1873+
cryptLibrary = InvokeNativeFunction.loadLibrary(this, PythonOS.getPythonOS() != PythonOS.PLATFORM_DARWIN ? "libcrypt.so" : null);
1874+
} catch (Throwable e) {
1875+
CompilerDirectives.transferToInterpreterAndInvalidate();
1876+
throw PRaiseNode.raiseUncached(invokeNode, PythonBuiltinClassType.SystemError, ErrorMessages.UNABLE_TO_LOAD_LIBCRYPT);
1877+
}
1878+
}
1879+
PosixNativeFunction function = PosixNativeFunction.crypt;
1880+
if (injectBranchProbability(SLOWPATH_PROBABILITY, cachedFunctions.get(function.ordinal()) == null)) {
1881+
InvokeNativeFunction.loadFunction(this, cryptLibrary, function);
1882+
}
1883+
Object funObject = cachedFunctions.get(function.ordinal());
18521884
/*
18531885
* From the manpage: Upon successful completion, crypt returns a pointer to a string which
18541886
* encodes both the hashed passphrase, and the settings that were used to encode it. See
@@ -1857,15 +1889,26 @@ public TruffleString crypt(TruffleString word, TruffleString salt,
18571889
* safe to call crypt from multiple threads simultaneously. Upon error, it may return a NULL
18581890
* pointer or a pointer to an invalid hash, depending on the implementation.
18591891
*/
1860-
// Note GIL is not enough as crypt is using global memory so we need a really global lock
1892+
// Note GIL is not enough as crypt is using global memory, so we need a really global lock
18611893
synchronized (CRYPT_LOCK) {
1862-
long resultPtr = invokeNode.callLong(this, PosixNativeFunction.call_crypt, stringToUTF8CString(word, switchEncodingToUtf8Node, copyToByteArrayNode),
1863-
stringToUTF8CString(salt, switchEncodingToUtf8Node, copyToByteArrayNode), wrap(lenArray));
1894+
long resultPtr;
1895+
Object[] args = new Object[]{
1896+
stringToUTF8CString(word, switchEncodingToUtf8Node, copyToByteArrayNode),
1897+
stringToUTF8CString(salt, switchEncodingToUtf8Node, copyToByteArrayNode)};
1898+
try {
1899+
Object interopResult = invokeNode.functionInterop.execute(funObject, args);
1900+
resultPtr = invokeNode.getResultInterop().asLong(interopResult);
1901+
} catch (UnsupportedTypeException | ArityException | UnsupportedMessageException e) {
1902+
throw CompilerDirectives.shouldNotReachHere(e);
1903+
}
18641904
// CPython doesn't handle the case of "invalid hash" return specially and neither do we
18651905
if (resultPtr == 0) {
18661906
throw getErrnoAndThrowPosixException(invokeNode);
18671907
}
1868-
int len = lenArray[0];
1908+
int len = 0;
1909+
while (UNSAFE.getByte(resultPtr + len) != 0) {
1910+
len++;
1911+
}
18691912
byte[] resultBytes = new byte[len];
18701913
UNSAFE.copyMemory(null, resultPtr, resultBytes, Unsafe.ARRAY_BYTE_BASE_OFFSET, len);
18711914
return createString(resultBytes, 0, resultBytes.length, false, fromByteArrayNode, switchEncodingFromUtf8Node);

graalpython/python-libposix/src/posix.c

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* The Universal Permissive License (UPL), Version 1.0
@@ -896,15 +896,6 @@ int32_t get_addrinfo_members(int64_t ptr, int32_t *intData, int64_t *longData, i
896896
return 0;
897897
}
898898

899-
int64_t call_crypt(const char *word, const char *salt, int32_t *len) {
900-
const char *result = crypt(word, salt);
901-
if (result == NULL) {
902-
return 0;
903-
}
904-
*len = strlen(result);
905-
return (int64_t)(uintptr_t)result;
906-
}
907-
908899
sem_t* call_sem_open(const char *name, int32_t openFlags, int32_t mode, int32_t value) {
909900
sem_t* result = sem_open(name, openFlags, mode, value);
910901
if (result == (sem_t*)SEM_FAILED) {

mx.graalpython/suite.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -852,7 +852,7 @@
852852
},
853853
"<others>": {
854854
"<others>": {
855-
"ldlibs": ["-lutil", "-lcrypt"],
855+
"ldlibs": ["-lutil"],
856856
"defaultBuild": True,
857857
},
858858
},

0 commit comments

Comments
 (0)