FORTIFY_SOURCE: add strlcpy / strlcat support

Add strlcpy / strlcat support to FORTIFY_SOURCE. This allows
us to do consistency checks on to ensure we don't overflow buffers
when the compiler is able to tell us the size of the buffer we're
dealing with.

Unlike previous changes, this change DOES NOT use the compiler's
builtin support. Instead, we do everything the compiler would
normally do.

Change-Id: I47c099a911382452eafd711f8e9bfe7c2d0a0d22
diff --git a/libc/include/string.h b/libc/include/string.h
index e1718e9..b8f60e4 100644
--- a/libc/include/string.h
+++ b/libc/include/string.h
@@ -122,6 +122,68 @@
     return __builtin___memset_chk(s, c, n, __builtin_object_size (s, 0));
 }
 
+extern size_t strlcpy_real(char *, const char *, size_t)
+    __asm__(__USER_LABEL_PREFIX__ "strlcpy");
+extern void __strlcpy_error()
+    __attribute__((error ("strlcpy called with size bigger than buffer")));
+extern size_t __strlcpy_chk(char *, const char *, size_t, size_t);
+
+__BIONIC_FORTIFY_INLINE
+size_t strlcpy(char *dest, const char *src, size_t size) {
+    size_t bos = __builtin_object_size(dest, 0);
+
+    // Compiler doesn't know destination size. Don't call __strlcpy_chk
+    if (bos == (size_t) -1) {
+        return strlcpy_real(dest, src, size);
+    }
+
+    // Compiler can prove, at compile time, that the passed in size
+    // is always <= the actual object size. Don't call __strlcpy_chk
+    if (__builtin_constant_p(size) && (size <= bos)) {
+        return strlcpy_real(dest, src, size);
+    }
+
+    // Compiler can prove, at compile time, that the passed in size
+    // is always > the actual object size. Force a compiler error.
+    if (__builtin_constant_p(size) && (size > bos)) {
+        __strlcpy_error();
+    }
+
+    return __strlcpy_chk(dest, src, size, bos);
+}
+
+extern size_t strlcat_real(char *, const char *, size_t)
+    __asm__(__USER_LABEL_PREFIX__ "strlcat");
+extern void __strlcat_error()
+    __attribute__((error ("strlcat called with size bigger than buffer")));
+extern size_t __strlcat_chk(char *, const char *, size_t, size_t);
+
+
+__BIONIC_FORTIFY_INLINE
+size_t strlcat(char *dest, const char *src, size_t size) {
+    size_t bos = __builtin_object_size(dest, 0);
+
+    // Compiler doesn't know destination size. Don't call __strlcat_chk
+    if (bos == (size_t) -1) {
+        return strlcat_real(dest, src, size);
+    }
+
+    // Compiler can prove, at compile time, that the passed in size
+    // is always <= the actual object size. Don't call __strlcat_chk
+    if (__builtin_constant_p(size) && (size <= bos)) {
+        return strlcat_real(dest, src, size);
+    }
+
+    // Compiler can prove, at compile time, that the passed in size
+    // is always > the actual object size. Force a compiler error.
+    if (__builtin_constant_p(size) && (size > bos)) {
+        __strlcat_error();
+    }
+
+    return __strlcat_chk(dest, src, size, bos);
+}
+
+
 #endif /* defined(__BIONIC_FORTIFY_INLINE) */
 
 __END_DECLS