On Mon, 5 Jan 2026 16:46:32 GMT, Weijun Wang <[email protected]> wrote:
>>> Maybe we can call Integer.toUnsignedLong() on pw_uid and pw_gid? In
>>> UnixSystem, they are longs.
>>
>> Yes, I had thought about this, too. But, I think we will need to distinguish
>> between the platforms. PPC64 and s390 (and also SPARC which is no longer
>> supported) pass smaller integer type values as 64 bit `long` and
>> `Integer.toUnsignedLong()` would be correct when we change the signature to
>> use `long` instead of `int`.
>> However, other platforms may require to pass the values as 4 Byte `int`.
>> Especially when they are passed on stack. (Not sure if any supported
>> platform does that. Probably not.) It's unfortunate that the FFM doesn't
>> provide a good abstraction for passing `uint32_t`. Maybe we should implement
>> an enhancement?
>> In hotspot, `CCallingConventionRequiresIntsAsLongs` is used to decide if 4
>> Byte integer types need to get extended.
>>
>>> I would suggest we just throw an exception in UnixSystem if getpwuid_r
>>> cannot find the username.
>>
>> Fine with me. I don't like ignoring getpwuid_r failures, either.
>
> I created fake account and group on my linux-x64 with numbers bigger than
> `Integer.MAX_VALUE` and call `getgroups` and `getpwuid_r`. The results
> always look good after a `Integer.toUnsignedLong()` conversion.
>
> I would think it's safe because it's only called after the C functions.
What you have done is fine. Thanks! However, there is one potential problem
left:
We are passing `tmpUid` to `getpwuid_r` as an `int`. That results in the
following sequence (example from AIX):
[2.537s][trace][foreign,downcall] ;; { argument shuffle
[2.537s][trace][foreign,downcall] 0x0a0001000747d744: mr r12,r3
[2.537s][trace][foreign,downcall] 0x0a0001000747d748: extsw r3,r4
[2.537s][trace][foreign,downcall] 0x0a0001000747d74c: mr r4,r5
[2.537s][trace][foreign,downcall] 0x0a0001000747d750: mr r5,r6
[2.537s][trace][foreign,downcall] 0x0a0001000747d754: extsw r6,r7
[2.537s][trace][foreign,downcall] 0x0a0001000747d758: mr r7,r8
[2.537s][trace][foreign,downcall] ;; } argument shuffle
The 4 Byte value for `tmpUid` is taken from Register r4, sign extended to 8
Byte long and put into the first argument register r3. The sign extend is wrong
because `uid` is an `uint32_t`. That violates the calling convention. We have
no way to tell the FFM that we need zero extend.
A possible workaround would be to do the conversion in Java and passing it as
long:
diff --git
a/src/jdk.security.auth/share/classes/com/sun/security/auth/module/UnixSystem.java
b/src/jdk.security.auth/share/classes/com/sun/security/auth/module/UnixSystem.java
index ed520529ac8..573513b7bef 100644
---
a/src/jdk.security.auth/share/classes/com/sun/security/auth/module/UnixSystem.java
+++
b/src/jdk.security.auth/share/classes/com/sun/security/auth/module/UnixSystem.java
@@ -25,6 +25,7 @@
package com.sun.security.auth.module;
+import jdk.internal.util.Architecture;
import jdk.internal.util.OperatingSystem;
import java.lang.foreign.AddressLayout;
@@ -83,6 +84,8 @@ public class UnixSystem {
= (ValueLayout.OfByte) LINKER.canonicalLayouts().get("char");
private static final ValueLayout.OfInt C_INT
= (ValueLayout.OfInt) LINKER.canonicalLayouts().get("int");
+ private static final ValueLayout.OfLong C_LONG
+ = (ValueLayout.OfLong) LINKER.canonicalLayouts().get("long");
private static final AddressLayout C_POINTER
= ((AddressLayout) LINKER.canonicalLayouts().get("void*"))
.withTargetLayout(MemoryLayout.sequenceLayout(java.lang.Long.MAX_VALUE,
C_CHAR));
@@ -110,10 +113,14 @@ public class UnixSystem {
// getpwuid_r does not work on AIX, instead we use another similar function
// extern int _posix_getpwuid_r(uid_t, struct passwd *, char *, int,
struct passwd **)
+ // At least the following architecture require appropriate zero or sign
extension to 64 bit.
+ private static final boolean calling_convention_requires_int_as_long =
Architecture.isPPC64() || Architecture.isPPC64LE() || Architecture.isS390();
private static final MethodHandle getpwuid_r = LINKER
.downcallHandle(SYMBOL_LOOKUP.findOrThrow(
OperatingSystem.isAix() ? "_posix_getpwuid_r" :
"getpwuid_r"),
- FunctionDescriptor.of(C_INT, C_INT, C_POINTER, C_POINTER,
+ FunctionDescriptor.of(C_INT,
+ calling_convention_requires_int_as_long ? C_LONG :
C_INT,
+ C_POINTER, C_POINTER,
OperatingSystem.isAix() ? C_INT : C_SIZE_T,
C_POINTER));
@@ -179,11 +186,15 @@ public UnixSystem() {
var pwd = scope.allocate(passwd_layout);
var result = scope.allocate(C_POINTER);
var buffer = scope.allocate(GETPW_R_SIZE_MAX);
- var tmpUid = (int)getuid.invokeExact();
+ long tmpUid = Integer.toUnsignedLong((int) getuid.invokeExact());
// Do not call invokeExact because the type of buffer_size is not
// always long in the underlying system.
- int out = (int) getpwuid_r.invoke(
- tmpUid, pwd, buffer, GETPW_R_SIZE_MAX, result);
+ int out = 0;
+ if (calling_convention_requires_int_as_long) {
+ out = (int) getpwuid_r.invoke(tmpUid, pwd, buffer,
GETPW_R_SIZE_MAX, result);
+ } else {
+ out = (int) getpwuid_r.invoke((int) tmpUid, pwd, buffer,
GETPW_R_SIZE_MAX, result);
+ }
if (out != 0 || result.get(ValueLayout.ADDRESS,
0).equals(MemorySegment.NULL)) {
if (out != 0) {
// If ERANGE (Result too large) is detected in a new
platform,
@@ -193,7 +204,7 @@ public UnixSystem() {
} else {
getpwuid_r_error = "the requested entry is not found";
}
- uid = Integer.toUnsignedLong(tmpUid);
+ uid = tmpUid;
gid = Integer.toUnsignedLong((int)getgid.invokeExact());
username = null;
} else {
But I don't really like it. I hope we can find a better solution.
-------------
PR Review Comment: https://git.openjdk.org/jdk/pull/28931#discussion_r2662361633