Skip to content

Commit 31f7093

Browse files
committed
HADOOP-9439. JniBasedUnixGroupsMapping: fix some crash bugs (Colin Patrick McCabe)
git-svn-id: https://svn.apache.org/repos/asf/hadoop/common/branches/branch-2.1-beta@1496117 13f79535-47bb-0310-9956-ffa450edef68
1 parent 05acd7c commit 31f7093

File tree

15 files changed

+688
-270
lines changed

15 files changed

+688
-270
lines changed

hadoop-common-project/hadoop-common/CHANGES.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,9 @@ Release 2.1.0-beta - UNRELEASED
256256
HADOOP-9624. TestFSMainOperationsLocalFileSystem failed when the Hadoop test
257257
root path has "X" in its name. (Xi Fang via cnauroth)
258258

259+
HADOOP-9439. JniBasedUnixGroupsMapping: fix some crash bugs. (Colin
260+
Patrick McCabe)
261+
259262
BREAKDOWN OF HADOOP-8562 SUBTASKS AND RELATED JIRAS
260263

261264
HADOOP-8924. Hadoop Common creating package-info.java must not depend on

hadoop-common-project/hadoop-common/src/CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,8 @@ add_dual_library(hadoop
180180
${D}/net/unix/DomainSocket.c
181181
${D}/security/JniBasedUnixGroupsMapping.c
182182
${D}/security/JniBasedUnixGroupsNetgroupMapping.c
183-
${D}/security/getGroup.c
183+
${D}/security/hadoop_group_info.c
184+
${D}/security/hadoop_user_info.c
184185
${D}/util/NativeCodeLoader.c
185186
${D}/util/NativeCrc32.c
186187
${D}/util/bulk_crc32.c

hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/io/nativeio/NativeIO.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ public static class POSIX {
9898

9999
static final String WORKAROUND_NON_THREADSAFE_CALLS_KEY =
100100
"hadoop.workaround.non.threadsafe.getpwuid";
101-
static final boolean WORKAROUND_NON_THREADSAFE_CALLS_DEFAULT = false;
101+
static final boolean WORKAROUND_NON_THREADSAFE_CALLS_DEFAULT = true;
102102

103103
private static long cacheTimeout = -1;
104104

hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/JniBasedUnixGroupsMapping.java

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,22 +40,44 @@ public class JniBasedUnixGroupsMapping implements GroupMappingServiceProvider {
4040

4141
private static final Log LOG =
4242
LogFactory.getLog(JniBasedUnixGroupsMapping.class);
43-
44-
native String[] getGroupForUser(String user);
45-
43+
4644
static {
4745
if (!NativeCodeLoader.isNativeCodeLoaded()) {
4846
throw new RuntimeException("Bailing out since native library couldn't " +
4947
"be loaded");
5048
}
49+
anchorNative();
5150
LOG.debug("Using JniBasedUnixGroupsMapping for Group resolution");
5251
}
5352

53+
/**
54+
* Set up our JNI resources.
55+
*
56+
* @throws RuntimeException if setup fails.
57+
*/
58+
native static void anchorNative();
59+
60+
/**
61+
* Get the set of groups associated with a user.
62+
*
63+
* @param username The user name
64+
*
65+
* @return The set of groups associated with a user.
66+
*/
67+
native static String[] getGroupsForUser(String username);
68+
69+
/**
70+
* Log an error message about a group. Used from JNI.
71+
*/
72+
static private void logError(int groupId, String error) {
73+
LOG.error("error looking up the name of group " + groupId + ": " + error);
74+
}
75+
5476
@Override
5577
public List<String> getGroups(String user) throws IOException {
5678
String[] groups = new String[0];
5779
try {
58-
groups = getGroupForUser(user);
80+
groups = getGroupsForUser(user);
5981
} catch (Exception e) {
6082
if (LOG.isDebugEnabled()) {
6183
LOG.debug("Error getting groups for " + user, e);

hadoop-common-project/hadoop-common/src/main/native/src/exception.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,3 +107,12 @@ jthrowable newIOException(JNIEnv* env, const char *fmt, ...)
107107
va_end(ap);
108108
return jthr;
109109
}
110+
111+
const char* terror(int errnum)
112+
{
113+
if ((errnum < 0) || (errnum >= sys_nerr)) {
114+
return "unknown error.";
115+
}
116+
return sys_errlist[errnum];
117+
}
118+

hadoop-common-project/hadoop-common/src/main/native/src/exception.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,4 +79,12 @@ jthrowable newRuntimeException(JNIEnv* env, const char *fmt, ...)
7979
jthrowable newIOException(JNIEnv* env, const char *fmt, ...)
8080
__attribute__((format(printf, 2, 3)));
8181

82+
/**
83+
* Thread-safe strerror alternative.
84+
*
85+
* @param errnum Error number.
86+
* @return Statically allocated error string.
87+
*/
88+
const char* terror(int errnum);
89+
8290
#endif

hadoop-common-project/hadoop-common/src/main/native/src/org/apache/hadoop/io/nativeio/NativeIO.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ static jmethodID nioe_ctor;
5959
// the monitor used for working around non-threadsafe implementations
6060
// of getpwuid_r, observed on platforms including RHEL 6.0.
6161
// Please see HADOOP-7156 for details.
62-
static jobject pw_lock_object;
62+
jobject pw_lock_object;
6363

6464
// Internal functions
6565
static void throw_ioe(JNIEnv* env, int errnum);

hadoop-common-project/hadoop-common/src/main/native/src/org/apache/hadoop/net/unix/DomainSocket.c

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -107,14 +107,6 @@ static jthrowable newSocketException(JNIEnv *env, int errnum,
107107
return jthr;
108108
}
109109

110-
static const char* terror(int errnum)
111-
{
112-
if ((errnum < 0) || (errnum >= sys_nerr)) {
113-
return "unknown error.";
114-
}
115-
return sys_errlist[errnum];
116-
}
117-
118110
/**
119111
* Flexible buffer that will try to fit data on the stack, and fall back
120112
* to the heap if necessary.

hadoop-common-project/hadoop-common/src/main/native/src/org/apache/hadoop/security/JniBasedUnixGroupsMapping.c

Lines changed: 149 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -30,88 +30,173 @@
3030
#include <pwd.h>
3131
#include <string.h>
3232

33+
#include "exception.h"
3334
#include "org_apache_hadoop_security_JniBasedUnixGroupsMapping.h"
3435
#include "org_apache_hadoop.h"
36+
#include "hadoop_group_info.h"
37+
#include "hadoop_user_info.h"
3538

36-
static jobjectArray emptyGroups = NULL;
39+
static jmethodID g_log_error_method;
3740

38-
JNIEXPORT jobjectArray JNICALL
39-
Java_org_apache_hadoop_security_JniBasedUnixGroupsMapping_getGroupForUser
40-
(JNIEnv *env, jobject jobj, jstring juser) {
41-
extern int getGroupIDList(const char *user, int *ngroups, gid_t **groups);
42-
extern int getGroupDetails(gid_t group, char **grpBuf);
43-
const char *cuser = NULL;
44-
jobjectArray jgroups = NULL;
45-
int error = -1;
41+
static jclass g_string_clazz;
4642

47-
if (emptyGroups == NULL) {
48-
jobjectArray lEmptyGroups = (jobjectArray)(*env)->NewObjectArray(env, 0,
49-
(*env)->FindClass(env, "java/lang/String"), NULL);
50-
if (lEmptyGroups == NULL) {
51-
goto cleanup;
52-
}
53-
emptyGroups = (*env)->NewGlobalRef(env, lEmptyGroups);
54-
if (emptyGroups == NULL) {
55-
goto cleanup;
56-
}
43+
extern jobject pw_lock_object;
44+
45+
JNIEXPORT void JNICALL
46+
Java_org_apache_hadoop_security_JniBasedUnixGroupsMapping_anchorNative(
47+
JNIEnv *env, jclass clazz)
48+
{
49+
jobject string_clazz;
50+
51+
g_log_error_method = (*env)->GetStaticMethodID(env, clazz, "logError",
52+
"(ILjava/lang/String;)V");
53+
if (!g_log_error_method) {
54+
return; // an exception has been raised
5755
}
58-
char *grpBuf = NULL;
59-
cuser = (*env)->GetStringUTFChars(env, juser, NULL);
60-
if (cuser == NULL) {
61-
goto cleanup;
56+
string_clazz = (*env)->FindClass(env, "java/lang/String");
57+
if (!string_clazz) {
58+
return; // an exception has been raised
6259
}
63-
64-
/*Get the number of the groups, and their IDs, this user belongs to*/
65-
gid_t *groups = NULL;
66-
int ngroups = 0;
67-
error = getGroupIDList(cuser, &ngroups, &groups);
68-
if (error != 0) {
69-
goto cleanup;
60+
g_string_clazz = (*env)->NewGlobalRef(env, string_clazz);
61+
if (!g_string_clazz) {
62+
jthrowable jthr = newRuntimeException(env,
63+
"JniBasedUnixGroupsMapping#anchorNative: failed to make "
64+
"a global reference to the java.lang.String class\n");
65+
(*env)->Throw(env, jthr);
66+
return;
7067
}
68+
}
7169

72-
jgroups = (jobjectArray)(*env)->NewObjectArray(env, ngroups,
73-
(*env)->FindClass(env, "java/lang/String"), NULL);
74-
if (jgroups == NULL) {
75-
error = -1;
76-
goto cleanup;
70+
/**
71+
* Log an error about a failure to look up a group ID
72+
*
73+
* @param env The JNI environment
74+
* @param clazz JniBasedUnixGroupsMapping class
75+
* @param gid The gid we failed to look up
76+
* @param ret Failure code
77+
*/
78+
static void logError(JNIEnv *env, jclass clazz, jint gid, int ret)
79+
{
80+
jstring error_msg;
81+
82+
error_msg = (*env)->NewStringUTF(env, terror(ret));
83+
if (!error_msg) {
84+
(*env)->ExceptionClear(env);
85+
return;
86+
}
87+
(*env)->CallStaticVoidMethod(env, clazz, g_log_error_method, gid, error_msg);
88+
if ((*env)->ExceptionCheck(env)) {
89+
(*env)->ExceptionClear(env);
90+
return;
7791
}
92+
(*env)->DeleteLocalRef(env, error_msg);
93+
}
7894

79-
/*Iterate over the groupIDs and get the group structure for each*/
80-
int i = 0;
81-
for (i = 0; i < ngroups; i++) {
82-
error = getGroupDetails(groups[i],&grpBuf);
83-
if (error != 0) {
84-
goto cleanup;
85-
}
86-
jstring jgrp = (*env)->NewStringUTF(env, ((struct group*)grpBuf)->gr_name);
87-
if (jgrp == NULL) {
88-
error = -1;
89-
goto cleanup;
95+
JNIEXPORT jobjectArray JNICALL
96+
Java_org_apache_hadoop_security_JniBasedUnixGroupsMapping_getGroupsForUser
97+
(JNIEnv *env, jclass clazz, jstring jusername)
98+
{
99+
const char *username = NULL;
100+
struct hadoop_user_info *uinfo = NULL;
101+
struct hadoop_group_info *ginfo = NULL;
102+
jstring jgroupname = NULL;
103+
int i, ret, nvalid;
104+
int pw_lock_locked = 0;
105+
jobjectArray jgroups = NULL, jnewgroups = NULL;
106+
107+
if (pw_lock_object != NULL) {
108+
if ((*env)->MonitorEnter(env, pw_lock_object) != JNI_OK) {
109+
goto done; // exception thrown
90110
}
91-
(*env)->SetObjectArrayElement(env, jgroups,i,jgrp);
92-
free(grpBuf);
93-
grpBuf = NULL;
111+
pw_lock_locked = 1;
94112
}
95-
96-
cleanup:
97-
if (error == ENOMEM) {
113+
username = (*env)->GetStringUTFChars(env, jusername, NULL);
114+
if (username == NULL) {
115+
goto done; // exception thrown
116+
}
117+
uinfo = hadoop_user_info_alloc();
118+
if (!uinfo) {
98119
THROW(env, "java/lang/OutOfMemoryError", NULL);
120+
goto done;
121+
}
122+
ret = hadoop_user_info_fetch(uinfo, username);
123+
if (ret == ENOENT) {
124+
jgroups = (*env)->NewObjectArray(env, 0, g_string_clazz, NULL);
125+
goto done;
126+
}
127+
ginfo = hadoop_group_info_alloc();
128+
if (!ginfo) {
129+
THROW(env, "java/lang/OutOfMemoryError", NULL);
130+
goto done;
131+
}
132+
ret = hadoop_user_info_getgroups(uinfo);
133+
if (ret) {
134+
if (ret == ENOMEM) {
135+
THROW(env, "java/lang/OutOfMemoryError", NULL);
136+
} else {
137+
char buf[128];
138+
snprintf(buf, sizeof(buf), "getgrouplist error %d (%s)",
139+
ret, terror(ret));
140+
THROW(env, "java/lang/RuntimeException", buf);
141+
}
142+
goto done;
143+
}
144+
jgroups = (jobjectArray)(*env)->NewObjectArray(env, uinfo->num_gids,
145+
g_string_clazz, NULL);
146+
for (nvalid = 0, i = 0; i < uinfo->num_gids; i++) {
147+
ret = hadoop_group_info_fetch(ginfo, uinfo->gids[i]);
148+
if (ret) {
149+
logError(env, clazz, uinfo->gids[i], ret);
150+
} else {
151+
jgroupname = (*env)->NewStringUTF(env, ginfo->group.gr_name);
152+
if (!jgroupname) { // exception raised
153+
(*env)->DeleteLocalRef(env, jgroups);
154+
jgroups = NULL;
155+
goto done;
156+
}
157+
(*env)->SetObjectArrayElement(env, jgroups, nvalid++, jgroupname);
158+
// We delete the local reference once the element is in the array.
159+
// This is OK because the array has a reference to it.
160+
// Technically JNI only mandates that the JVM allow up to 16 local
161+
// references at a time (though many JVMs allow more than that.)
162+
(*env)->DeleteLocalRef(env, jgroupname);
163+
}
164+
}
165+
if (nvalid != uinfo->num_gids) {
166+
// If some group names could not be looked up, allocate a smaller array
167+
// with just the entries that could be resolved. Java has no equivalent to
168+
// realloc, so we have to do this manually.
169+
jnewgroups = (jobjectArray)(*env)->NewObjectArray(env, nvalid,
170+
(*env)->FindClass(env, "java/lang/String"), NULL);
171+
if (!jnewgroups) { // exception raised
172+
(*env)->DeleteLocalRef(env, jgroups);
173+
jgroups = NULL;
174+
goto done;
175+
}
176+
for (i = 0; i < nvalid; i++) {
177+
jgroupname = (*env)->GetObjectArrayElement(env, jgroups, i);
178+
(*env)->SetObjectArrayElement(env, jnewgroups, i, jgroupname);
179+
(*env)->DeleteLocalRef(env, jgroupname);
180+
}
181+
(*env)->DeleteLocalRef(env, jgroups);
182+
jgroups = jnewgroups;
99183
}
100-
if (error == ENOENT) {
101-
THROW(env, "java/io/IOException", "No entry for user");
184+
185+
done:
186+
if (pw_lock_locked) {
187+
(*env)->MonitorExit(env, pw_lock_object);
102188
}
103-
if (groups != NULL) {
104-
free(groups);
189+
if (username) {
190+
(*env)->ReleaseStringUTFChars(env, jusername, username);
105191
}
106-
if (grpBuf != NULL) {
107-
free(grpBuf);
192+
if (uinfo) {
193+
hadoop_user_info_free(uinfo);
108194
}
109-
if (cuser != NULL) {
110-
(*env)->ReleaseStringUTFChars(env, juser, cuser);
195+
if (ginfo) {
196+
hadoop_group_info_free(ginfo);
111197
}
112-
if (error == 0) {
113-
return jgroups;
114-
} else {
115-
return emptyGroups;
198+
if (jgroupname) {
199+
(*env)->DeleteLocalRef(env, jgroupname);
116200
}
201+
return jgroups;
117202
}

hadoop-common-project/hadoop-common/src/main/native/src/org/apache/hadoop/security/JniBasedUnixGroupsMappingWin.c

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,13 @@
2525

2626
static jobjectArray emptyGroups = NULL;
2727

28+
JNIEXPORT void JNICALL
29+
Java_org_apache_hadoop_security_JniBasedUnixGroupsMapping_anchorNative(
30+
JNIEnv *env, jclass clazz)
31+
{
32+
// no-op until full port of HADOOP-9439 to Windows is available
33+
}
34+
2835
/*
2936
* Throw a java.IO.IOException, generating the message from errno.
3037
*/
@@ -57,8 +64,8 @@ static void throw_ioexception(JNIEnv* env, DWORD errnum)
5764
}
5865

5966
JNIEXPORT jobjectArray JNICALL
60-
Java_org_apache_hadoop_security_JniBasedUnixGroupsMapping_getGroupForUser
61-
(JNIEnv *env, jobject jobj, jstring juser) {
67+
Java_org_apache_hadoop_security_JniBasedUnixGroupsMapping_getGroupsForUser
68+
(JNIEnv *env, jclass clazz, jstring juser) {
6269
const WCHAR *user = NULL;
6370
jobjectArray jgroups = NULL;
6471
DWORD dwRtnCode = ERROR_SUCCESS;

0 commit comments

Comments
 (0)