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

Reply via email to