Fix brk/sbrk error checking.

Note that the kernel returns the current break on error or if the requested
break is smaller than the minimum break, or the new break. I don't know where
we got the idea that the kernel could return -1.

Also optimizes the query case.

Also hides an accidentally-exported symbol for LP64.

Change-Id: I0fd6b8b14ddf1ae82935c0c3fc610da5cc74932e
diff --git a/tests/unistd_test.cpp b/tests/unistd_test.cpp
index 70b23bd..f56b767 100644
--- a/tests/unistd_test.cpp
+++ b/tests/unistd_test.cpp
@@ -18,6 +18,8 @@
 #include "ScopedSignalHandler.h"
 #include "TemporaryFile.h"
 
+#define __STDC_LIMIT_MACROS // For glibc.
+
 #include <errno.h>
 #include <fcntl.h>
 #include <stdint.h>
@@ -29,14 +31,49 @@
   ASSERT_GT(sysconf(_SC_MONOTONIC_CLOCK), 0);
 }
 
-TEST(unistd, sbrk) {
-  void* initial_break = sbrk(0);
+static void* get_brk() {
+  return sbrk(0);
+}
 
-  void* new_break = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(initial_break) + 2000);
+static void* page_align(uintptr_t addr) {
+  uintptr_t mask = sysconf(_SC_PAGE_SIZE) - 1;
+  return reinterpret_cast<void*>((addr + mask) & ~mask);
+}
+
+TEST(unistd, brk) {
+  void* initial_break = get_brk();
+
+  // The kernel aligns the break to a page.
+  void* new_break = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(initial_break) + 1);
   ASSERT_EQ(0, brk(new_break));
+  ASSERT_GE(get_brk(), new_break);
 
-  void* final_break = sbrk(0);
-  ASSERT_EQ(final_break, new_break);
+  new_break = page_align(reinterpret_cast<uintptr_t>(initial_break) + sysconf(_SC_PAGE_SIZE));
+  ASSERT_EQ(0, brk(new_break));
+  ASSERT_EQ(get_brk(), new_break);
+}
+
+TEST(unistd, brk_ENOMEM) {
+  ASSERT_EQ(-1, brk(reinterpret_cast<void*>(-1)));
+  ASSERT_EQ(ENOMEM, errno);
+}
+
+TEST(unistd, sbrk_ENOMEM) {
+  intptr_t current_brk = reinterpret_cast<intptr_t>(get_brk());
+
+  // Can't increase by so much that we'd overflow.
+  ASSERT_EQ(reinterpret_cast<void*>(-1), sbrk(PTRDIFF_MAX));
+  ASSERT_EQ(ENOMEM, errno);
+
+  // Can't reduce by more than the current break.
+  ASSERT_EQ(reinterpret_cast<void*>(-1), sbrk(-(current_brk + 1)));
+  ASSERT_EQ(ENOMEM, errno);
+
+#if !defined(__GLIBC__)
+  // The maximum negative value is an interesting special case that glibc gets wrong.
+  ASSERT_EQ(reinterpret_cast<void*>(-1), sbrk(PTRDIFF_MIN));
+  ASSERT_EQ(ENOMEM, errno);
+#endif
 }
 
 TEST(unistd, truncate) {