chiark / gitweb /
util-lib: wrap personality() to fix up broken glibc error handling (#6766)
authorLennart Poettering <lennart@poettering.net>
Fri, 8 Sep 2017 14:16:29 +0000 (16:16 +0200)
committerSven Eden <yamakuzure@gmx.net>
Fri, 8 Sep 2017 14:16:29 +0000 (16:16 +0200)
glibc appears to propagate different errors in different ways, let's fix
this up, so that our own code doesn't get confused by this.

See #6752 + #6737 for details.

Fixes: #6755
src/basic/process-util.c
src/basic/process-util.h

index 98f5a38ae5789d0b9bb99ceeabcc99537c503821..06675427e9a5949ba56b8c274c82e413d1c30c89 100644 (file)
@@ -913,6 +913,28 @@ const char* personality_to_string(unsigned long p) {
         return architecture_to_string(architecture);
 }
 
+int safe_personality(unsigned long p) {
+        int ret;
+
+        /* So here's the deal, personality() is weirdly defined by glibc. In some cases it returns a failure via errno,
+         * and in others as negative return value containing an errno-like value. Let's work around this: this is a
+         * wrapper that uses errno if it is set, and uses the return value otherwise. And then it sets both errno and
+         * the return value indicating the same issue, so that we are definitely on the safe side.
+         *
+         * See https://github.com/elogind/elogind/issues/6737 */
+
+        errno = 0;
+        ret = personality(p);
+        if (ret < 0) {
+                if (errno != 0)
+                        return -errno;
+
+                errno = -ret;
+        }
+
+        return ret;
+}
+
 int opinionated_personality(unsigned long *ret) {
         int current;
 
@@ -920,9 +942,9 @@ int opinionated_personality(unsigned long *ret) {
          * opinionated though, and ignores all the finer-grained bits and exotic personalities, only distinguishing the
          * two most relevant personalities: PER_LINUX and PER_LINUX32. */
 
-        current = personality(PERSONALITY_INVALID);
+        current = safe_personality(PERSONALITY_INVALID);
         if (current < 0)
-                return -errno;
+                return current;
 
         if (((unsigned long) current & 0xffff) == PER_LINUX32)
                 *ret = PER_LINUX32;
index 339dc9f59dce5415fd0f46e07988b9a06af69d69..71b4732322b6223cbf22af8fd9bd6238d2412bab 100644 (file)
@@ -100,6 +100,7 @@ bool oom_score_adjust_is_valid(int oa);
 unsigned long personality_from_string(const char *p);
 const char *personality_to_string(unsigned long);
 
+int safe_personality(unsigned long p);
 int opinionated_personality(unsigned long *ret);
 
 int ioprio_class_to_string_alloc(int i, char **s);