about summary refs log tree commit diff
diff options
context:
space:
mode:
authorJakub Jelinek <jakub@redhat.com>2008-04-30 12:11:31 +0000
committerJakub Jelinek <jakub@redhat.com>2008-04-30 12:11:31 +0000
commitef73dbc1301bc42c132d15ae6ca866233c0beeb4 (patch)
tree7386ab88c4f2c43ac417a2ea0b0b19239bdc0b19
parent14e44061ad888abbea7239599a9109b41776e41c (diff)
downloadglibc-ef73dbc1301bc42c132d15ae6ca866233c0beeb4.tar.gz
glibc-ef73dbc1301bc42c132d15ae6ca866233c0beeb4.tar.xz
glibc-ef73dbc1301bc42c132d15ae6ca866233c0beeb4.zip
-rw-r--r--ChangeLog40
-rw-r--r--fedora/glibc.spec.in7
-rw-r--r--nptl/ChangeLog5
-rw-r--r--nptl/sysdeps/unix/sysv/linux/sparc/sparc32/sem_wait.c2
-rw-r--r--nscd/aicache.c9
-rw-r--r--nscd/cache.c11
-rw-r--r--nscd/connections.c47
-rw-r--r--nscd/grpcache.c9
-rw-r--r--nscd/hstcache.c9
-rw-r--r--nscd/initgrcache.c9
-rw-r--r--nscd/mem.c33
-rw-r--r--nscd/nscd.h30
-rw-r--r--nscd/pwdcache.c9
-rw-r--r--nscd/servicescache.c9
-rw-r--r--sysdeps/unix/sysv/linux/sparc/sparc64/semctl.c2
15 files changed, 203 insertions, 28 deletions
diff --git a/ChangeLog b/ChangeLog
index f1969c4e33..826b080137 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,43 @@
+2008-04-25  David S. Miller  <davem@davemloft.net>
+
+	* sysdeps/unix/sysv/linux/sparc/sparc64/semctl.c (semctl):
+	Pass "union semun" properly in to sys_ipc, it must be passed
+	by value, not by reference.
+
+2008-04-22  Jakub Jelinek  <jakub@redhat.com>
+
+	* nscd/Makefile (nscd-cflags): Set back to -fpie.
+	* nscd/nscd.h (mem_in_flight): Add attribute_tls_model_ie.
+	* nscd/connections.c (mem_in_flight): Likewise.
+
+	* nscd/nscd.h (dbs): Make hidden.
+
+2008-04-15  Ulrich Drepper  <drepper@redhat.com>
+
+	[BZ #5381]
+	* nscd/nscd.h: Define enum in_flight, mem_in_flight, and
+	mem_in_flight_list variables.  Add new parameter to mempool_alloc
+	prototype.
+	* nscd/mem.c (mempool_alloc): Take additional parameter.  Initialize
+	appropriate mem_in_flight element.
+	(gc): Take allocations which have not yet been committed to the
+	database into account.
+	* nscd/cache.c (cache_add): Add new parameter to mempool_alloc call.
+	Reset mem_in_flight before returning.
+	* nscd/connections.c (nscd_run_worker): Initialize mem_in_flight and
+	cue it up in mem_in_flight_list.
+	* nscd/aicache.c: Adjust mempool_alloc call.
+	* nscd/grpcache.c: Likewise.
+	* nscd/hstcache.c: Likewise.
+	* nscd/initgrcache.c: Likewise.
+	* nscd/pwdcache.c: Likewise.
+	* nscd/servicescache.c: Likewise.
+	* nscd/Makefile (nscd-flags): Until ld is fixed, use -fpic instead
+	of -fpie.
+
+	* nscd/connections.c (handle_request): Provide better error message
+	in case SELinux forbids the service.
+
 2008-04-11  Ulrich Drepper  <drepper@redhat.com>
 
 	* sysdeps/unix/sysv/linux/powerpc/bits/mathinline.h (__signbitl):
diff --git a/fedora/glibc.spec.in b/fedora/glibc.spec.in
index 2b39ca9e26..c58ba1b48f 100644
--- a/fedora/glibc.spec.in
+++ b/fedora/glibc.spec.in
@@ -19,7 +19,7 @@
 Summary: The GNU libc libraries
 Name: glibc
 Version: @glibcversion@
-Release: 1
+Release: 2
 # GPLv2+ is used in a bunch of programs, LGPLv2+ is used for libraries.
 # Things that are linked directly into dynamically linked programs
 # and shared libraries (e.g. crt files, lib*_nonshared.a) have an additional
@@ -976,6 +976,11 @@ rm -f *.filelist*
 %endif
 
 %changelog
+* Wed Apr 30 2008 Jakub Jelinek <jakub@redhat.com> 2.8-2
+- fix nscd races during GC (BZ#5381)
+- rebuilt with fixed GCC to fix regex miscompilation on power6
+- SPARC fixes
+
 * Sat Apr 12 2008 Jakub Jelinek <jakub@redhat.com> 2.8-1
 - 2.8 release
 
diff --git a/nptl/ChangeLog b/nptl/ChangeLog
index 63b0eab9c3..ba94a95dac 100644
--- a/nptl/ChangeLog
+++ b/nptl/ChangeLog
@@ -1,3 +1,8 @@
+2008-04-14  David S. Miller  <davem@davemloft.net>
+
+	* sysdeps/unix/sysv/linux/sparc/sparc32/sem_wait.c
+	(__old_sem_wait): Fix argument to lll_futex_wait().
+
 2007-11-26  Daniel Jacobowitz  <dan@codesourcery.com>
 
 	* pthread_create.c: Require pthread_mutex_trylock and
diff --git a/nptl/sysdeps/unix/sysv/linux/sparc/sparc32/sem_wait.c b/nptl/sysdeps/unix/sysv/linux/sparc/sparc32/sem_wait.c
index 3c71c969b8..b14f976a61 100644
--- a/nptl/sysdeps/unix/sysv/linux/sparc/sparc32/sem_wait.c
+++ b/nptl/sysdeps/unix/sysv/linux/sparc/sparc32/sem_wait.c
@@ -155,7 +155,7 @@ __old_sem_wait (sem_t *sem)
       /* Enable asynchronous cancellation.  Required by the standard.  */
       int oldtype = __pthread_enable_asynccancel ();
 
-      err = lll_futex_wait (futex, 0,
+      err = lll_futex_wait (&isem->value, 0,
 			    isem->private ^ FUTEX_PRIVATE_FLAG);
 
       /* Disable asynchronous cancellation.  */
diff --git a/nscd/aicache.c b/nscd/aicache.c
index a69a7781d3..3de84821a4 100644
--- a/nscd/aicache.c
+++ b/nscd/aicache.c
@@ -262,7 +262,8 @@ addhstaiX (struct database_dyn *db, int fd, request_header *req,
 		{
 		  dataset = (struct dataset *) mempool_alloc (db,
 							      total
-							      + req->key_len);
+							      + req->key_len,
+							      IDX_result_data);
 		  if (dataset == NULL)
 		    ++db->head->addfailed;
 		}
@@ -338,7 +339,8 @@ addhstaiX (struct database_dyn *db, int fd, request_header *req,
 		      struct dataset *newp
 			= (struct dataset *) mempool_alloc (db,
 							    total
-							    + req->key_len);
+							    + req->key_len,
+							    IDX_result_data);
 		      if (__builtin_expect (newp != NULL, 1))
 			{
 			  /* Adjust pointer into the memory block.  */
@@ -424,7 +426,8 @@ addhstaiX (struct database_dyn *db, int fd, request_header *req,
       if (fd != -1)
 	TEMP_FAILURE_RETRY (send (fd, &notfound, total, MSG_NOSIGNAL));
 
-      dataset = mempool_alloc (db, sizeof (struct dataset) + req->key_len);
+      dataset = mempool_alloc (db, sizeof (struct dataset) + req->key_len,
+			       IDX_result_data);
       /* If we cannot permanently store the result, so be it.  */
       if (dataset != NULL)
 	{
diff --git a/nscd/cache.c b/nscd/cache.c
index 12c4f01e40..7e9b0dc2df 100644
--- a/nscd/cache.c
+++ b/nscd/cache.c
@@ -155,11 +155,16 @@ cache_add (int type, const void *key, size_t len, struct datahead *packet,
   unsigned long int hash = __nis_hash (key, len) % table->head->module;
   struct hashentry *newp;
 
-  newp = mempool_alloc (table, sizeof (struct hashentry));
+  newp = mempool_alloc (table, sizeof (struct hashentry), IDX_record_data);
   /* If we cannot allocate memory, just do not do anything.  */
   if (newp == NULL)
     {
       ++table->head->addfailed;
+
+      /* Mark the in-flight memory as unused.  */
+      for (enum in_flight idx = 0; idx < IDX_record_data; ++idx)
+	mem_in_flight.block[idx].dbidx = -1;
+
       return -1;
     }
 
@@ -215,6 +220,10 @@ cache_add (int type, const void *key, size_t len, struct datahead *packet,
     else
       next_wakeup = table->wakeup_time;
 
+  /* Mark the in-flight memory as unused.  */
+  for (enum in_flight idx = 0; idx < IDX_last; ++idx)
+    mem_in_flight.block[idx].dbidx = -1;
+
   return 0;
 }
 
diff --git a/nscd/connections.c b/nscd/connections.c
index 5da5e5f08a..15148bdf3d 100644
--- a/nscd/connections.c
+++ b/nscd/connections.c
@@ -225,6 +225,11 @@ static int sock;
 /* Number of times clients had to wait.  */
 unsigned long int client_queued;
 
+/* Data structure for recording in-flight memory allocation.  */
+__thread struct mem_in_flight mem_in_flight attribute_tls_model_ie;
+/* Global list of the mem_in_flight variables of all the threads.  */
+struct mem_in_flight *mem_in_flight_list;
+
 
 ssize_t
 writeall (int fd, const void *buf, size_t len)
@@ -964,7 +969,7 @@ send_ro_fd (struct database_dyn *db, char *key, int fd)
 
 /* Handle new request.  */
 static void
-handle_request (int fd, request_header *req, void *key, uid_t uid)
+handle_request (int fd, request_header *req, void *key, uid_t uid, pid_t pid)
 {
   if (__builtin_expect (req->version, NSCD_VERSION) != NSCD_VERSION)
     {
@@ -979,7 +984,31 @@ cannot handle old request version %d; current version is %d"),
   if (selinux_enabled && nscd_request_avc_has_perm (fd, req->type) != 0)
     {
       if (debug_level > 0)
-	dbg_log (_("request not handled due to missing permission"));
+	{
+#ifdef SO_PEERCRED
+# ifdef PATH_MAX
+	  char buf[PATH_MAX];
+# else
+	  char buf[4096];
+# endif
+
+	  snprintf (buf, sizeof (buf), "/proc/%ld/exe", (long int) pid);
+	  ssize_t n = readlink (buf, buf, sizeof (buf) - 1);
+
+	  if (n <= 0)
+	    dbg_log (_("\
+request from %ld not handled due to missing permission"), (long int) pid);
+	  else
+	    {
+	      buf[n] = '\0';
+	      dbg_log (_("\
+request from '%s' [%ld] not handled due to missing permission"),
+		       buf, (long int) pid);
+	    }
+#else
+	  dbg_log (_("request not handled due to missing permission"));
+#endif
+	}
       return;
     }
 
@@ -1426,6 +1455,16 @@ nscd_run_worker (void *p)
 {
   char buf[256];
 
+  /* Initialize the memory-in-flight list.  */
+  for (enum in_flight idx = 0; idx < IDX_last; ++idx)
+    mem_in_flight.block[idx].dbidx = -1;
+  /* And queue this threads structure.  */
+  do
+    mem_in_flight.next = mem_in_flight_list;
+  while (atomic_compare_and_exchange_bool_acq (&mem_in_flight_list,
+					       &mem_in_flight,
+					       mem_in_flight.next) != 0);
+
   /* Initial locking.  */
   pthread_mutex_lock (&readylist_lock);
 
@@ -1491,6 +1530,8 @@ nscd_run_worker (void *p)
 	  if (getsockopt (fd, SOL_SOCKET, SO_PEERCRED, &caller, &optlen) == 0)
 	    pid = caller.pid;
 	}
+#else
+      const pid_t pid = 0;
 #endif
 
       /* It should not be possible to crash the nscd with a silly
@@ -1531,7 +1572,7 @@ handle_request: request received (Version = %d)"), req.version);
 	    }
 
 	  /* Phew, we got all the data, now process it.  */
-	  handle_request (fd, &req, keybuf, uid);
+	  handle_request (fd, &req, keybuf, uid, pid);
 	}
 
     close_and_out:
diff --git a/nscd/grpcache.c b/nscd/grpcache.c
index 002f04fb3e..57fcd0f7d2 100644
--- a/nscd/grpcache.c
+++ b/nscd/grpcache.c
@@ -113,7 +113,8 @@ cache_addgr (struct database_dyn *db, int fd, request_header *req,
 	  written = TEMP_FAILURE_RETRY (send (fd, &notfound, total,
 					      MSG_NOSIGNAL));
 
-	  dataset = mempool_alloc (db, sizeof (struct dataset) + req->key_len);
+	  dataset = mempool_alloc (db, sizeof (struct dataset) + req->key_len,
+				   IDX_result_data);
 	  /* If we cannot permanently store the result, so be it.  */
 	  if (dataset != NULL)
 	    {
@@ -204,7 +205,8 @@ cache_addgr (struct database_dyn *db, int fd, request_header *req,
 
       if (he == NULL)
 	{
-	  dataset = (struct dataset *) mempool_alloc (db, total + n);
+	  dataset = (struct dataset *) mempool_alloc (db, total + n,
+						      IDX_result_data);
 	  if (dataset == NULL)
 	    ++db->head->addfailed;
 	}
@@ -274,7 +276,8 @@ cache_addgr (struct database_dyn *db, int fd, request_header *req,
 	      /* We have to create a new record.  Just allocate
 		 appropriate memory and copy it.  */
 	      struct dataset *newp
-		= (struct dataset *) mempool_alloc (db, total + n);
+		= (struct dataset *) mempool_alloc (db, total + n,
+						    IDX_result_data);
 	      if (newp != NULL)
 		{
 		  /* Adjust pointers into the memory block.  */
diff --git a/nscd/hstcache.c b/nscd/hstcache.c
index cc041581e1..7777723463 100644
--- a/nscd/hstcache.c
+++ b/nscd/hstcache.c
@@ -121,7 +121,8 @@ cache_addhst (struct database_dyn *db, int fd, request_header *req,
 	    written = TEMP_FAILURE_RETRY (send (fd, &notfound, total,
 						MSG_NOSIGNAL));
 
-	  dataset = mempool_alloc (db, sizeof (struct dataset) + req->key_len);
+	  dataset = mempool_alloc (db, sizeof (struct dataset) + req->key_len,
+				   IDX_result_data);
 	  /* If we cannot permanently store the result, so be it.  */
 	  if (dataset != NULL)
 	    {
@@ -226,7 +227,8 @@ cache_addhst (struct database_dyn *db, int fd, request_header *req,
       if (he == NULL && h_addr_list_cnt == 1)
 	{
 	  dataset = (struct dataset *) mempool_alloc (db,
-						      total + req->key_len);
+						      total + req->key_len,
+						      IDX_result_data);
 	  if (dataset == NULL)
 	    ++db->head->addfailed;
 	}
@@ -312,7 +314,8 @@ cache_addhst (struct database_dyn *db, int fd, request_header *req,
 		     appropriate memory and copy it.  */
 		  struct dataset *newp
 		    = (struct dataset *) mempool_alloc (db,
-							total + req->key_len);
+							total + req->key_len,
+							IDX_result_data);
 		  if (newp != NULL)
 		    {
 		      /* Adjust pointers into the memory block.  */
diff --git a/nscd/initgrcache.c b/nscd/initgrcache.c
index 157cd7860c..6a95fb5992 100644
--- a/nscd/initgrcache.c
+++ b/nscd/initgrcache.c
@@ -197,7 +197,8 @@ addinitgroupsX (struct database_dyn *db, int fd, request_header *req,
 	    written = TEMP_FAILURE_RETRY (send (fd, &notfound, total,
 						MSG_NOSIGNAL));
 
-	  dataset = mempool_alloc (db, sizeof (struct dataset) + req->key_len);
+	  dataset = mempool_alloc (db, sizeof (struct dataset) + req->key_len,
+				   IDX_result_data);
 	  /* If we cannot permanently store the result, so be it.  */
 	  if (dataset != NULL)
 	    {
@@ -259,7 +260,8 @@ addinitgroupsX (struct database_dyn *db, int fd, request_header *req,
       if (he == NULL)
 	{
 	  dataset = (struct dataset *) mempool_alloc (db,
-						      total + req->key_len);
+						      total + req->key_len,
+						      IDX_result_data);
 	  if (dataset == NULL)
 	    ++db->head->addfailed;
 	}
@@ -329,7 +331,8 @@ addinitgroupsX (struct database_dyn *db, int fd, request_header *req,
 	      /* We have to create a new record.  Just allocate
 		 appropriate memory and copy it.  */
 	      struct dataset *newp
-		= (struct dataset *) mempool_alloc (db, total + req->key_len);
+		= (struct dataset *) mempool_alloc (db, total + req->key_len,
+						    IDX_result_data);
 	      if (newp != NULL)
 		{
 		  /* Adjust pointer into the memory block.  */
diff --git a/nscd/mem.c b/nscd/mem.c
index 048e3ddd32..508d9343d0 100644
--- a/nscd/mem.c
+++ b/nscd/mem.c
@@ -197,6 +197,31 @@ gc (struct database_dyn *db)
     }
   assert (cnt == db->head->nentries);
 
+  /* Go through the list of in-flight memory blocks.  */
+  struct mem_in_flight *mrunp = mem_in_flight_list;
+  while (mrunp != NULL)
+    {
+      /* NB: There can be no race between this test and another thread
+        setting the field to the index we are looking for because
+        this would require the other thread to also have the memlock
+        for the database.
+
+	NB2: we do not have to look at latter blocks (higher indices) if
+	earlier blocks are not in flight.  They are always allocated in
+	sequence.  */
+      for (enum in_flight idx = IDX_result_data;
+	   idx < IDX_last && mrunp->block[idx].dbidx == db - dbs; ++idx)
+	{
+	 assert ((char *) mrunp->block[idx].blockaddr > db->data);
+	 assert ((char *) mrunp->block[idx].blockaddr
+		 + mrunp->block[0].blocklen <= db->data + db->memsize);
+	 markrange (mark, (char *) mrunp->block[idx].blockaddr -  db->data,
+		    mrunp->block[idx].blocklen);
+	}
+
+      mrunp = mrunp->next;
+    }
+
   /* Sort the entries by the addresses of the referenced data.  All
      the entries pointing to the same DATAHEAD object will have the
      same key.  Stability of the sorting is unimportant.  */
@@ -503,7 +528,7 @@ gc (struct database_dyn *db)
 
 
 void *
-mempool_alloc (struct database_dyn *db, size_t len)
+mempool_alloc (struct database_dyn *db, size_t len, enum in_flight idx)
 {
   /* Make sure LEN is a multiple of our maximum alignment so we can
      keep track of used memory is multiples of this alignment value.  */
@@ -567,6 +592,12 @@ mempool_alloc (struct database_dyn *db, size_t len)
       db->head->first_free += len;
 
       db->last_alloc_failed = false;
+
+      /* Remember that we have allocated this memory.  */
+      assert (idx >= 0 && idx < IDX_last);
+      mem_in_flight.block[idx].dbidx = db - dbs;
+      mem_in_flight.block[idx].blocklen = len;
+      mem_in_flight.block[idx].blockaddr = res;
     }
 
   pthread_mutex_unlock (&db->memlock);
diff --git a/nscd/nscd.h b/nscd/nscd.h
index ec2d9454ca..66813e7480 100644
--- a/nscd/nscd.h
+++ b/nscd/nscd.h
@@ -130,7 +130,7 @@ struct database_dyn
 
 
 /* Global variables.  */
-extern struct database_dyn dbs[lastdb];
+extern struct database_dyn dbs[lastdb] attribute_hidden;
 extern const char *const dbnames[lastdb];
 extern const char *const serv2str[LASTREQ];
 
@@ -181,6 +181,31 @@ extern uid_t old_uid;
 extern gid_t old_gid;
 
 
+/* Memory allocation in flight.  Each thread can have a limited number
+   of allocation in flight.  No need to create dynamic data
+   structures.  We use fixed indices.  */
+enum in_flight
+  {
+    IDX_result_data = 0,
+    /* Keep the IDX_record_data entry last at all times.  */
+    IDX_record_data = 1,
+    IDX_last
+  };
+extern __thread struct mem_in_flight
+{
+  struct
+  {
+    int dbidx;
+    nscd_ssize_t blocklen;
+    void *blockaddr;
+  } block[IDX_last];
+
+  struct mem_in_flight *next;
+} mem_in_flight attribute_tls_model_ie;
+/* Global list of the mem_in_flight variables of all the threads.  */
+extern struct mem_in_flight *mem_in_flight_list;
+
+
 /* Prototypes for global functions.  */
 
 /* nscd.c */
@@ -271,7 +296,8 @@ extern void readdservbyport (struct database_dyn *db, struct hashentry *he,
 			     struct datahead *dh);
 
 /* mem.c */
-extern void *mempool_alloc (struct database_dyn *db, size_t len);
+extern void *mempool_alloc (struct database_dyn *db, size_t len,
+			    enum in_flight idx);
 extern void gc (struct database_dyn *db);
 
 
diff --git a/nscd/pwdcache.c b/nscd/pwdcache.c
index bc1b6bab2f..3d1e8241ec 100644
--- a/nscd/pwdcache.c
+++ b/nscd/pwdcache.c
@@ -120,7 +120,8 @@ cache_addpw (struct database_dyn *db, int fd, request_header *req,
 	    written = TEMP_FAILURE_RETRY (send (fd, &notfound, total,
 						MSG_NOSIGNAL));
 
-	  dataset = mempool_alloc (db, sizeof (struct dataset) + req->key_len);
+	  dataset = mempool_alloc (db, sizeof (struct dataset) + req->key_len,
+				   IDX_result_data);
 	  /* If we cannot permanently store the result, so be it.  */
 	  if (dataset != NULL)
 	    {
@@ -199,7 +200,8 @@ cache_addpw (struct database_dyn *db, int fd, request_header *req,
 
       if (he == NULL)
 	{
-	  dataset = (struct dataset *) mempool_alloc (db, total + n);
+	  dataset = (struct dataset *) mempool_alloc (db, total + n,
+						      IDX_result_data);
 	  if (dataset == NULL)
 	    ++db->head->addfailed;
 	}
@@ -270,7 +272,8 @@ cache_addpw (struct database_dyn *db, int fd, request_header *req,
 	      /* We have to create a new record.  Just allocate
 		 appropriate memory and copy it.  */
 	      struct dataset *newp
-		= (struct dataset *) mempool_alloc (db, total + n);
+		= (struct dataset *) mempool_alloc (db, total + n,
+						    IDX_result_data);
 	      if (newp != NULL)
 		{
 		  /* Adjust pointer into the memory block.  */
diff --git a/nscd/servicescache.c b/nscd/servicescache.c
index e122cb3f16..e2d1e26a2f 100644
--- a/nscd/servicescache.c
+++ b/nscd/servicescache.c
@@ -103,7 +103,8 @@ cache_addserv (struct database_dyn *db, int fd, request_header *req,
 	  written = TEMP_FAILURE_RETRY (send (fd, &notfound, total,
 					      MSG_NOSIGNAL));
 
-	  dataset = mempool_alloc (db, sizeof (struct dataset) + req->key_len);
+	  dataset = mempool_alloc (db, sizeof (struct dataset) + req->key_len,
+				   IDX_result_data);
 	  /* If we cannot permanently store the result, so be it.  */
 	  if (dataset != NULL)
 	    {
@@ -190,7 +191,8 @@ cache_addserv (struct database_dyn *db, int fd, request_header *req,
       if (he == NULL)
 	{
 	  dataset = (struct dataset *) mempool_alloc (db,
-						      total + req->key_len);
+						      total + req->key_len,
+						      IDX_result_data);
 	  if (dataset == NULL)
 	    ++db->head->addfailed;
 	}
@@ -261,7 +263,8 @@ cache_addserv (struct database_dyn *db, int fd, request_header *req,
 	      /* We have to create a new record.  Just allocate
 		 appropriate memory and copy it.  */
 	      struct dataset *newp
-		= (struct dataset *) mempool_alloc (db, total + req->key_len);
+		= (struct dataset *) mempool_alloc (db, total + req->key_len,
+						    IDX_result_data);
 	      if (newp != NULL)
 		{
 		  /* Adjust pointers into the memory block.  */
diff --git a/sysdeps/unix/sysv/linux/sparc/sparc64/semctl.c b/sysdeps/unix/sysv/linux/sparc/sparc64/semctl.c
index 057e287828..4f826b156d 100644
--- a/sysdeps/unix/sysv/linux/sparc/sparc64/semctl.c
+++ b/sysdeps/unix/sysv/linux/sparc/sparc64/semctl.c
@@ -54,5 +54,5 @@ semctl (int semid, int semnum, int cmd, ...)
   va_end (ap);
 
   return INLINE_SYSCALL (ipc, 5, IPCOP_semctl, semid, semnum, cmd,
-			 CHECK_SEMCTL (&arg, semid, cmd));
+			 CHECK_SEMCTL (&arg, semid, cmd)->array);
 }