Skip to content

Commit ddbc44e

Browse files
committed
Initialize _Py_HashSecret
1 parent 5416f71 commit ddbc44e

File tree

5 files changed

+75
-0
lines changed

5 files changed

+75
-0
lines changed

graalpython/com.oracle.graal.python.cext/src/pyhash.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,13 @@ Py_hash_t _Py_HashDouble(double value) {
4747
long _PyHASH_INF;
4848
long _PyHASH_NAN;
4949
long _PyHASH_IMAG;
50+
_Py_HashSecret_t _Py_HashSecret = {{0}};
5051

5152
void initialize_hashes() {
5253
_PyHASH_INF = UPCALL_L(PY_BUILTIN, polyglot_from_string("hash", SRC_CS), INFINITY);
5354
_PyHASH_NAN = UPCALL_L(PY_BUILTIN, polyglot_from_string("hash", SRC_CS), NAN);
5455
_PyHASH_IMAG = UPCALL_L(PY_TRUFFLE_CEXT, polyglot_from_string("PyHash_Imag", SRC_CS));
56+
((void (*)(int8_t *))polyglot_get_member(PY_TRUFFLE_CEXT, polyglot_from_string("PyTruffleHash_InitSecret", SRC_CS)))(polyglot_from_i8_array((int8_t *)&_Py_HashSecret, sizeof(_Py_HashSecret)));
5557
}
5658

5759
Py_hash_t _Py_HashBytes(const void *src, Py_ssize_t len) {

graalpython/com.oracle.graal.python.shell/src/com/oracle/graal/python/shell/GraalPythonMain.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -532,6 +532,11 @@ protected void launch(Builder contextBuilder) {
532532
unbufferedIO = unbufferedIO || System.getenv("PYTHONUNBUFFERED") != null;
533533
dontWriteBytecode = dontWriteBytecode || System.getenv("PYTHONDONTWRITEBYTECODE") != null;
534534

535+
String hashSeed = System.getenv("PYTHONHASHSEED");
536+
if (hashSeed != null) {
537+
contextBuilder.option("python.HashSeed", hashSeed);
538+
}
539+
535540
String envWarnOptions = System.getenv("PYTHONWARNINGS");
536541
if (envWarnOptions != null && !envWarnOptions.isEmpty()) {
537542
if (warnOptions == null) {

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/PythonCextBuiltins.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1453,6 +1453,26 @@ long getHash() {
14531453
}
14541454
}
14551455

1456+
@Builtin(name = "PyTruffleHash_InitSecret", minNumOfPositionalArgs = 1)
1457+
@GenerateNodeFactory
1458+
abstract static class PyTruffleHashGetSecret extends PythonUnaryBuiltinNode {
1459+
@Specialization
1460+
@TruffleBoundary
1461+
Object get(Object secretPtr) {
1462+
try {
1463+
InteropLibrary lib = InteropLibrary.getUncached(secretPtr);
1464+
byte[] secret = getContext().getHashSecret();
1465+
int len = (int) lib.getArraySize(secretPtr);
1466+
for (int i = 0; i < len; i++) {
1467+
lib.writeArrayElement(secretPtr, i, secret[i]);
1468+
}
1469+
return 0;
1470+
} catch (UnsupportedMessageException | UnsupportedTypeException | InvalidArrayIndexException e) {
1471+
throw CompilerDirectives.shouldNotReachHere(e);
1472+
}
1473+
}
1474+
}
1475+
14561476
@Builtin(name = "PyTruffleFrame_New", minNumOfPositionalArgs = 4)
14571477
@GenerateNodeFactory
14581478
abstract static class PyTruffleFrameNewNode extends PythonBuiltinNode {

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

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@
3737
import java.io.PrintWriter;
3838
import java.lang.ref.WeakReference;
3939
import java.nio.file.LinkOption;
40+
import java.security.NoSuchAlgorithmException;
41+
import java.security.SecureRandom;
4042
import java.text.MessageFormat;
4143
import java.util.ArrayDeque;
4244
import java.util.ArrayList;
@@ -419,6 +421,9 @@ PythonThreadState getThreadState() {
419421
private final ThreadGroup threadGroup = new ThreadGroup(GRAALPYTHON_THREADS);
420422
private final IDUtils idUtils = new IDUtils();
421423

424+
// Equivalent of _Py_HashSecret
425+
@CompilationFinal(dimensions = 1) private byte[] hashSecret = new byte[24];
426+
422427
// ctypes' used native libraries/functions.
423428
private final ConcurrentHashMap<Long, Object> ptrAdrMap = new ConcurrentHashMap<>();
424429

@@ -1074,6 +1079,11 @@ public long getPerfCounterStart() {
10741079
return perfCounterStart;
10751080
}
10761081

1082+
public byte[] getHashSecret() {
1083+
assert !ImageInfo.inImageBuildtimeCode();
1084+
return hashSecret;
1085+
}
1086+
10771087
public boolean isInitialized() {
10781088
if (PythonUtils.ASSERTIONS_ENABLED && isInitializedNonCompilationFinal != isInitialized) {
10791089
// We cannot use normal assertion, because those are removed in compilation
@@ -1229,6 +1239,9 @@ private void patchPackagePaths(String from, String to) {
12291239
}
12301240

12311241
private void setupRuntimeInformation(boolean isPatching) {
1242+
if (!ImageInfo.inImageBuildtimeCode()) {
1243+
initializeHashSecret();
1244+
}
12321245
nativeZlib = NFIZlibSupport.createNative(this, "");
12331246
nativeBz2lib = NFIBz2Support.createNative(this, "");
12341247
nativeLZMA = NFILZMASupport.createNative(this, "");
@@ -1255,6 +1268,38 @@ private void setupRuntimeInformation(boolean isPatching) {
12551268
isInitializedNonCompilationFinal = true;
12561269
}
12571270

1271+
private void initializeHashSecret() {
1272+
String hashSeed = getOption(PythonOptions.HashSeed);
1273+
if (hashSeed.equals("random")) {
1274+
try {
1275+
SecureRandom.getInstance("NativePRNGNonBlocking").nextBytes(hashSecret);
1276+
} catch (NoSuchAlgorithmException e) {
1277+
throw new RuntimeException("Unable to obtain entropy source for hash randomization (NativePRNGNonBlocking)");
1278+
}
1279+
} else {
1280+
try {
1281+
long hashSeedValue = Long.parseLong(hashSeed);
1282+
if (hashSeedValue < 0 || hashSeedValue > 4294967295L) {
1283+
throw new NumberFormatException();
1284+
}
1285+
// 0 disables the option, leaving the secret at 0
1286+
if (hashSeedValue != 0) {
1287+
// Generate the whole secret from the seed number the same way as CPython
1288+
// Taken from bootstrap_hash.c:lcg_urandom
1289+
int x = (int) hashSeedValue;
1290+
for (int i = 0; i < hashSecret.length; i++) {
1291+
x *= 214013;
1292+
x += 2531011;
1293+
/* modulo 2 ^ (8 * sizeof(int)) */
1294+
hashSecret[i] = (byte) ((x >>> 16) & 0xff);
1295+
}
1296+
}
1297+
} catch (NumberFormatException e) {
1298+
throw new RuntimeException("PYTHONHASHSEED must be \"random\" or an integer in range [0; 4294967295]");
1299+
}
1300+
}
1301+
}
1302+
12581303
private void initializePosixSupport() {
12591304
String option = getLanguage().getEngineOption(PythonOptions.PosixModuleBackend);
12601305
PosixSupport result;

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,9 @@ private PythonOptions() {
135135
@Option(category = OptionCategory.USER, help = "Equivalent to setting the PYTHONWARNINGS environment variable for the standard launcher.", stability = OptionStability.STABLE) //
136136
public static final OptionKey<String> WarnOptions = new OptionKey<>("");
137137

138+
@Option(category = OptionCategory.USER, help = "Equivalent to setting PYTHONHASHSEED environment variable", stability = OptionStability.STABLE) //
139+
public static final OptionKey<String> HashSeed = new OptionKey<>("random");
140+
138141
@EngineOption @Option(category = OptionCategory.USER, help = "Choose the backend for the POSIX module. Valid values are 'java', 'native', 'llvm'.") //
139142
public static final OptionKey<String> PosixModuleBackend = new OptionKey<>("java");
140143

0 commit comments

Comments
 (0)