Merge "Add argument checking to sigemptyset(3) and friends."
diff --git a/libc/include/signal.h b/libc/include/signal.h
index 6432c18..9d3badc 100644
--- a/libc/include/signal.h
+++ b/libc/include/signal.h
@@ -28,6 +28,7 @@
 #ifndef _SIGNAL_H_
 #define _SIGNAL_H_
 
+#include <errno.h>
 #include <sys/cdefs.h>
 #include <limits.h>		/* For LONG_BIT */
 #include <string.h>		/* For memset() */
@@ -53,45 +54,57 @@
 #  define _NSIG  64
 #endif
 
-extern const char * const sys_siglist[];
-extern const char * const sys_signame[];
+extern const char* const sys_siglist[];
+extern const char* const sys_signame[];
 
-static __inline__ int sigismember(sigset_t *set, int signum)
-{
-    unsigned long *local_set = (unsigned long *)set;
-    signum--;
-    return (int)((local_set[signum/LONG_BIT] >> (signum%LONG_BIT)) & 1);
+static __inline__ int sigismember(sigset_t* set, int signum) {
+  if (set == NULL || signum < 1 || signum >= 8*sizeof(sigset_t)) {
+    errno = EINVAL;
+    return -1;
+  }
+  unsigned long* local_set = (unsigned long*) set;
+  signum--;
+  return (int) ((local_set[signum/LONG_BIT] >> (signum%LONG_BIT)) & 1);
 }
 
-
-static __inline__ int sigaddset(sigset_t *set, int signum)
-{
-    unsigned long *local_set = (unsigned long *)set;
-    signum--;
-    local_set[signum/LONG_BIT] |= 1UL << (signum%LONG_BIT);
-    return 0;
+static __inline__ int sigaddset(sigset_t* set, int signum) {
+  if (set == NULL || signum < 1 || signum >= 8*sizeof(sigset_t)) {
+    errno = EINVAL;
+    return -1;
+  }
+  unsigned long* local_set = (unsigned long*) set;
+  signum--;
+  local_set[signum/LONG_BIT] |= 1UL << (signum%LONG_BIT);
+  return 0;
 }
 
-
-static __inline__ int sigdelset(sigset_t *set, int signum)
-{
-    unsigned long *local_set = (unsigned long *)set;
-    signum--;
-    local_set[signum/LONG_BIT] &= ~(1UL << (signum%LONG_BIT));
-    return 0;
+static __inline__ int sigdelset(sigset_t* set, int signum) {
+  if (set == NULL || signum < 1 || signum >= 8*sizeof(sigset_t)) {
+    errno = EINVAL;
+    return -1;
+  }
+  unsigned long* local_set = (unsigned long*) set;
+  signum--;
+  local_set[signum/LONG_BIT] &= ~(1UL << (signum%LONG_BIT));
+  return 0;
 }
 
-
-static __inline__ int sigemptyset(sigset_t *set)
-{
-    memset(set, 0, sizeof *set);
-    return 0;
+static __inline__ int sigemptyset(sigset_t* set) {
+  if (set == NULL) {
+    errno = EINVAL;
+    return -1;
+  }
+  memset(set, 0, sizeof *set);
+  return 0;
 }
 
-static __inline__ int sigfillset(sigset_t *set)
-{
-    memset(set, ~0, sizeof *set);
-    return 0;
+static __inline__ int sigfillset(sigset_t* set) {
+  if (set == NULL) {
+    errno = EINVAL;
+    return -1;
+  }
+  memset(set, ~0, sizeof *set);
+  return 0;
 }
 
 
diff --git a/tests/Android.mk b/tests/Android.mk
index 0da3951..abc6f52 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -59,6 +59,7 @@
     libgen_test.cpp \
     pthread_test.cpp \
     regex_test.cpp \
+    signal_test.cpp \
     stack_protector_test.cpp \
     stdio_test.cpp \
     stdlib_test.cpp \
diff --git a/tests/signal_test.cpp b/tests/signal_test.cpp
new file mode 100644
index 0000000..a54f14d
--- /dev/null
+++ b/tests/signal_test.cpp
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include <errno.h>
+#include <signal.h>
+
+template <typename Fn>
+static void TestSigSet1(Fn fn) {
+  // NULL sigset_t*.
+  sigset_t* set_ptr = NULL;
+  errno = 0;
+  ASSERT_EQ(-1, fn(set_ptr));
+  ASSERT_EQ(EINVAL, errno);
+
+  // Non-NULL.
+  sigset_t set;
+  errno = 0;
+  ASSERT_EQ(0, fn(&set));
+  ASSERT_EQ(0, errno);
+}
+
+template <typename Fn>
+static void TestSigSet2(Fn fn) {
+  // NULL sigset_t*.
+  sigset_t* set_ptr = NULL;
+  errno = 0;
+  ASSERT_EQ(-1, fn(set_ptr, SIGSEGV));
+  ASSERT_EQ(EINVAL, errno);
+
+  sigset_t set;
+  sigemptyset(&set);
+
+  int min_signal = SIGHUP;
+  int max_signal = SIGRTMAX;
+
+#if __BIONIC__
+  // bionic's sigset_t is too small: 32 bits instead of 64.
+  // This means you can't refer to any of the real-time signals.
+  // See http://b/3038348 and http://b/5828899.
+  max_signal = 31;
+#else
+  // Other C libraries are perfectly capable of using their largest signal.
+  ASSERT_GE(sizeof(sigset_t) * 8, static_cast<size_t>(SIGRTMAX));
+#endif
+
+  // Bad signal number: too small.
+  errno = 0;
+  ASSERT_EQ(-1, fn(&set, 0));
+  ASSERT_EQ(EINVAL, errno);
+
+  // Bad signal number: too high.
+  errno = 0;
+  ASSERT_EQ(-1, fn(&set, max_signal + 1));
+  ASSERT_EQ(EINVAL, errno);
+
+  // Good signal numbers, low and high ends of range.
+  errno = 0;
+  ASSERT_EQ(0, fn(&set, min_signal));
+  ASSERT_EQ(0, errno);
+  ASSERT_EQ(0, fn(&set, max_signal));
+  ASSERT_EQ(0, errno);
+}
+
+TEST(signal, sigismember_invalid) {
+  TestSigSet2(sigismember);
+}
+
+TEST(signal, sigaddset_invalid) {
+  TestSigSet2(sigaddset);
+}
+
+TEST(signal, sigdelset_invalid) {
+  TestSigSet2(sigdelset);
+}
+
+TEST(signal, sigemptyset_invalid) {
+  TestSigSet1(sigemptyset);
+}
+
+TEST(signal, sigfillset_invalid) {
+  TestSigSet1(sigfillset);
+}