Merge "libc fortify: make string.h use diagnose_if"
diff --git a/libc/include/bits/fortify/fcntl.h b/libc/include/bits/fortify/fcntl.h
index 1d3d7bc..3e0a590 100644
--- a/libc/include/bits/fortify/fcntl.h
+++ b/libc/include/bits/fortify/fcntl.h
@@ -48,11 +48,6 @@
int open(const char* pathname, int flags, mode_t modes, ...) __overloadable
__errorattr(__open_too_many_args_error);
-__BIONIC_ERROR_FUNCTION_VISIBILITY
-int open(const char* pathname, int flags) __overloadable
- __enable_if(flags & O_CREAT, __open_too_few_args_error)
- __errorattr(__open_too_few_args_error);
-
/*
* pass_object_size serves two purposes here, neither of which involve __bos: it
* disqualifies this function from having its address taken (so &open works),
@@ -60,30 +55,27 @@
* open(const char *, int, ...).
*/
__BIONIC_FORTIFY_INLINE
-int open(const char* const __pass_object_size pathname,
- int flags) __overloadable {
+int open(const char* const __pass_object_size pathname, int flags)
+ __overloadable
+ __clang_error_if(flags & O_CREAT, "'open' " __open_too_few_args_error) {
return __open_2(pathname, flags);
}
__BIONIC_FORTIFY_INLINE
-int open(const char* const __pass_object_size pathname, int flags, mode_t modes)
- __overloadable {
+int open(const char* const __pass_object_size pathname, int flags, mode_t modes) __overloadable {
return __open_real(pathname, flags, modes);
}
__BIONIC_ERROR_FUNCTION_VISIBILITY
-int openat(int dirfd, const char* pathname, int flags) __overloadable
- __enable_if(flags & O_CREAT, __open_too_few_args_error)
- __errorattr(__open_too_few_args_error);
-
-__BIONIC_ERROR_FUNCTION_VISIBILITY
int openat(int dirfd, const char* pathname, int flags, mode_t modes, ...)
__overloadable
__errorattr(__open_too_many_args_error);
__BIONIC_FORTIFY_INLINE
int openat(int dirfd, const char* const __pass_object_size pathname,
- int flags) __overloadable {
+ int flags)
+ __overloadable
+ __clang_error_if(flags & O_CREAT, "'openat' " __open_too_few_args_error) {
return __openat_2(dirfd, pathname, flags);
}
diff --git a/libc/include/bits/fortify/poll.h b/libc/include/bits/fortify/poll.h
index e9b52c8..8363e35 100644
--- a/libc/include/bits/fortify/poll.h
+++ b/libc/include/bits/fortify/poll.h
@@ -37,42 +37,31 @@
#if defined(__BIONIC_FORTIFY)
#if __ANDROID_API__ >= __ANDROID_API_M__
#if defined(__clang__)
-__BIONIC_ERROR_FUNCTION_VISIBILITY
-int poll(struct pollfd* fds, nfds_t fd_count, int timeout) __overloadable
- __enable_if(__bos(fds) != __BIONIC_FORTIFY_UNKNOWN_SIZE &&
- __bos(fds) < sizeof(*fds) * fd_count,
- "selected when there aren't fd_count fds")
- __errorattr("too many fds specified");
-
__BIONIC_FORTIFY_INLINE
-int poll(struct pollfd* const fds __pass_object_size, nfds_t fd_count,
- int timeout) __overloadable {
+int poll(struct pollfd* const fds __pass_object_size, nfds_t fd_count, int timeout)
+ __overloadable
+ __clang_error_if(__bos(fds) != __BIONIC_FORTIFY_UNKNOWN_SIZE &&
+ __bos(fds) < sizeof(*fds) * fd_count,
+ "in call to 'poll', fd_count is larger than the given buffer") {
size_t bos_fds = __bos(fds);
if (bos_fds == __BIONIC_FORTIFY_UNKNOWN_SIZE) {
- return __call_bypassing_fortify(poll)(fds, fd_count, timeout);
+ return __call_bypassing_fortify(poll)(fds, fd_count, timeout);
}
-
return __poll_chk(fds, fd_count, timeout, bos_fds);
}
-__BIONIC_ERROR_FUNCTION_VISIBILITY
-int ppoll(struct pollfd* fds, nfds_t fd_count, const struct timespec* timeout,
- const sigset_t* mask) __overloadable
- __enable_if(__bos(fds) != __BIONIC_FORTIFY_UNKNOWN_SIZE &&
- __bos(fds) < sizeof(*fds) * fd_count,
- "selected when there aren't fd_count fds")
- __errorattr("too many fds specified");
-
__BIONIC_FORTIFY_INLINE
-int ppoll(struct pollfd* const fds __pass_object_size, nfds_t fd_count,
- const struct timespec* timeout, const sigset_t* mask) __overloadable {
+int ppoll(struct pollfd* const fds __pass_object_size, nfds_t fd_count, const struct timespec* timeout, const sigset_t* mask)
+ __overloadable
+ __clang_error_if(__bos(fds) != __BIONIC_FORTIFY_UNKNOWN_SIZE &&
+ __bos(fds) < sizeof(*fds) * fd_count,
+ "in call to 'ppoll', fd_count is larger than the given buffer") {
size_t bos_fds = __bos(fds);
if (bos_fds == __BIONIC_FORTIFY_UNKNOWN_SIZE) {
- return __call_bypassing_fortify(ppoll)(fds, fd_count, timeout, mask);
+ return __call_bypassing_fortify(ppoll)(fds, fd_count, timeout, mask);
}
-
return __ppoll_chk(fds, fd_count, timeout, mask, bos_fds);
}
#else /* defined(__clang__) */
diff --git a/libc/include/bits/fortify/socket.h b/libc/include/bits/fortify/socket.h
index c9e9436..3e610d6 100644
--- a/libc/include/bits/fortify/socket.h
+++ b/libc/include/bits/fortify/socket.h
@@ -37,64 +37,55 @@
#if defined(__BIONIC_FORTIFY)
-#define __recvfrom_bad_size "recvfrom called with size bigger than buffer"
-#define __sendto_bad_size "sendto called with size bigger than buffer"
+#define __recvfrom_bad_size "'recvfrom' called with size bigger than buffer"
+#define __sendto_bad_size "'sendto' called with size bigger than buffer"
#if defined(__clang__)
#if __ANDROID_API__ >= __ANDROID_API_N__
-__BIONIC_ERROR_FUNCTION_VISIBILITY
-ssize_t recvfrom(int fd, void* const buf __pass_object_size0, size_t len,
- int flags, struct sockaddr* src_addr, socklen_t* addr_len)
- __overloadable
- __enable_if(__bos(buf) != __BIONIC_FORTIFY_UNKNOWN_SIZE &&
- __bos(buf) < len, "selected when the buffer is too small")
- __errorattr(__recvfrom_bad_size);
-
__BIONIC_FORTIFY_INLINE
-ssize_t recvfrom(int fd, void* const buf __pass_object_size0, size_t len,
- int flags, struct sockaddr* src_addr, socklen_t* addr_len)
- __overloadable {
+ssize_t recvfrom(int fd, void* const buf __pass_object_size0, size_t len, int flags, struct sockaddr* src_addr, socklen_t* addr_len)
+ __overloadable
+ __clang_error_if(__bos(buf) != __BIONIC_FORTIFY_UNKNOWN_SIZE && __bos(buf) < len,
+ __recvfrom_bad_size) {
size_t bos = __bos0(buf);
if (bos == __BIONIC_FORTIFY_UNKNOWN_SIZE) {
- return __call_bypassing_fortify(recvfrom)(fd, buf, len, flags, src_addr,
- addr_len);
+ return __call_bypassing_fortify(recvfrom)(fd, buf, len, flags, src_addr, addr_len);
}
-
return __recvfrom_chk(fd, buf, len, bos, flags, src_addr, addr_len);
}
#endif /* __ANDROID_API__ >= __ANDROID_API_N__ */
#if __ANDROID_API__ >= __ANDROID_API_N_MR1__
-__BIONIC_ERROR_FUNCTION_VISIBILITY
-ssize_t sendto(int fd, const void* buf, size_t len, int flags,
- const struct sockaddr* dest_addr, socklen_t addr_len)
- __overloadable
- __enable_if(__bos0(buf) != __BIONIC_FORTIFY_UNKNOWN_SIZE &&
- __bos0(buf) < len, "selected when the buffer is too small")
- __errorattr(__sendto_bad_size);
-
__BIONIC_FORTIFY_INLINE
-ssize_t sendto(int fd, const void* const buf __pass_object_size0, size_t len,
- int flags, const struct sockaddr* dest_addr, socklen_t addr_len)
- __overloadable {
+ssize_t sendto(int fd, const void* const buf __pass_object_size0, size_t len, int flags, const struct sockaddr* dest_addr, socklen_t addr_len)
+ __overloadable
+ __clang_error_if(__bos0(buf) != __BIONIC_FORTIFY_UNKNOWN_SIZE && __bos0(buf) < len,
+ __sendto_bad_size) {
size_t bos = __bos0(buf);
if (bos == __BIONIC_FORTIFY_UNKNOWN_SIZE) {
- return __call_bypassing_fortify(sendto)(fd, buf, len, flags, dest_addr,
- addr_len);
+ return __call_bypassing_fortify(sendto)(fd, buf, len, flags, dest_addr, addr_len);
}
-
return __sendto_chk(fd, buf, len, bos, flags, dest_addr, addr_len);
}
-
-__BIONIC_ERROR_FUNCTION_VISIBILITY
-ssize_t send(int socket, const void* buf, size_t len, int flags)
- __overloadable
- __enable_if(__bos0(buf) != __BIONIC_FORTIFY_UNKNOWN_SIZE &&
- __bos0(buf) < len, "selected when the buffer is too small")
- __errorattr("send called with size bigger than buffer");
#endif /* __ANDROID_API__ >= __ANDROID_API_N_MR1__ */
+__BIONIC_FORTIFY_INLINE
+ssize_t recv(int socket, void* const buf __pass_object_size0, size_t len, int flags)
+ __overloadable
+ __clang_error_if(__bos0(buf) != __BIONIC_FORTIFY_UNKNOWN_SIZE && __bos0(buf) < len,
+ "'recv' called with size bigger than buffer") {
+ return recvfrom(socket, buf, len, flags, NULL, 0);
+}
+
+__BIONIC_FORTIFY_INLINE
+ssize_t send(int socket, const void* const buf __pass_object_size0, size_t len, int flags)
+ __overloadable
+ __clang_error_if(__bos0(buf) != __BIONIC_FORTIFY_UNKNOWN_SIZE && __bos0(buf) < len,
+ "'send' called with size bigger than buffer") {
+ return sendto(socket, buf, len, flags, NULL, 0);
+}
+
#else /* defined(__clang__) */
ssize_t __recvfrom_real(int, void*, size_t, int, struct sockaddr*, socklen_t*) __RENAME(recvfrom);
__errordecl(__recvfrom_error, __recvfrom_bad_size);
@@ -147,20 +138,17 @@
}
#endif /* __ANDROID_API__ >= __ANDROID_API_N_MR1__ */
-#endif /* defined(__clang__) */
-#undef __recvfrom_bad_size
-#undef __sendto_bad_size
-
__BIONIC_FORTIFY_INLINE
-ssize_t recv(int socket, void* const buf __pass_object_size0, size_t len,
- int flags) __overloadable {
+ssize_t recv(int socket, void* buf, size_t len, int flags) {
return recvfrom(socket, buf, len, flags, NULL, 0);
}
__BIONIC_FORTIFY_INLINE
-ssize_t send(int socket, const void* const buf __pass_object_size0, size_t len, int flags)
- __overloadable {
+ssize_t send(int socket, const void* buf, size_t len, int flags) {
return sendto(socket, buf, len, flags, NULL, 0);
}
+#endif /* defined(__clang__) */
+#undef __recvfrom_bad_size
+#undef __sendto_bad_size
#endif /* __BIONIC_FORTIFY */
diff --git a/libc/include/bits/fortify/stat.h b/libc/include/bits/fortify/stat.h
index d119e2c..c168c38 100644
--- a/libc/include/bits/fortify/stat.h
+++ b/libc/include/bits/fortify/stat.h
@@ -33,23 +33,17 @@
mode_t __umask_chk(mode_t) __INTRODUCED_IN(18);
#if defined(__BIONIC_FORTIFY)
-#define __umask_invalid_mode_str "umask called with invalid mode"
+#define __umask_invalid_mode_str "'umask' called with invalid mode"
#if defined(__clang__)
#if __ANDROID_API__ >= __ANDROID_API_J_MR2__
-/*
- * Abuse enable_if to make these be seen as overloads of umask, rather than
- * definitions.
- */
-__BIONIC_ERROR_FUNCTION_VISIBILITY
-mode_t umask(mode_t mode) __overloadable
- __enable_if(1, "")
- __enable_if(mode & ~0777, __umask_invalid_mode_str)
- __errorattr(__umask_invalid_mode_str);
-
+/* Abuse enable_if to make this an overload of umask. */
__BIONIC_FORTIFY_INLINE
-mode_t umask(mode_t mode) __enable_if(1, "") __overloadable {
+mode_t umask(mode_t mode)
+ __overloadable
+ __enable_if(1, "")
+ __clang_error_if(mode & ~0777, __umask_invalid_mode_str) {
return __umask_chk(mode);
}
#endif /* __ANDROID_API__ >= __ANDROID_API_J_MR2__ */
diff --git a/libc/include/bits/fortify/stdio.h b/libc/include/bits/fortify/stdio.h
index 4acefd7..cfc78d7 100644
--- a/libc/include/bits/fortify/stdio.h
+++ b/libc/include/bits/fortify/stdio.h
@@ -38,13 +38,13 @@
#if __ANDROID_API__ >= __ANDROID_API_J_MR1__
__BIONIC_FORTIFY_INLINE __printflike(3, 0)
-int vsnprintf(char *const __pass_object_size dest, size_t size,
- const char * format, va_list ap) __overloadable {
+int vsnprintf(char* const __pass_object_size dest, size_t size, const char* format, va_list ap)
+ __overloadable {
return __builtin___vsnprintf_chk(dest, size, 0, __bos(dest), format, ap);
}
__BIONIC_FORTIFY_INLINE __printflike(2, 0)
-int vsprintf(char *const __pass_object_size dest, const char* format, va_list ap) __overloadable {
+int vsprintf(char* const __pass_object_size dest, const char* format, va_list ap) __overloadable {
return __builtin___vsprintf_chk(dest, 0, __bos(dest), format, ap);
}
#endif /* __ANDROID_API__ >= __ANDROID_API_J_MR1__ */
@@ -59,14 +59,14 @@
int snprintf(char* dest, size_t size, const char* format)
__overloadable
__enable_if(__bos(dest) != __BIONIC_FORTIFY_UNKNOWN_SIZE &&
- __bos(dest) < __builtin_strlen(format),
+ __bos(dest) < __builtin_strlen(format),
"format string will always overflow destination buffer")
__errorattr("format string will always overflow destination buffer");
__BIONIC_FORTIFY_INLINE
__printflike(3, 4)
-int snprintf(char* const __pass_object_size dest,
- size_t size, const char* format, ...) __overloadable {
+int snprintf(char* const __pass_object_size dest, size_t size, const char* format, ...)
+ __overloadable {
va_list va;
va_start(va, format);
int result = __builtin___vsnprintf_chk(dest, size, 0, __bos(dest), format, va);
@@ -75,7 +75,8 @@
}
__BIONIC_ERROR_FUNCTION_VISIBILITY
-int sprintf(char* dest, const char* format) __overloadable
+int sprintf(char* dest, const char* format)
+ __overloadable
__enable_if(__bos(dest) != __BIONIC_FORTIFY_UNKNOWN_SIZE &&
__bos(dest) < __builtin_strlen(format),
"format string will always overflow destination buffer")
@@ -94,44 +95,27 @@
#if __ANDROID_API__ >= __ANDROID_API_N__
__BIONIC_FORTIFY_INLINE
-size_t fread(void* buf, size_t size, size_t count,
- FILE* stream) __overloadable
- __enable_if(__unsafe_check_mul_overflow(size, count), "size * count overflows")
- __errorattr("size * count overflows");
-
-__BIONIC_FORTIFY_INLINE
-size_t fread(void* buf, size_t size, size_t count, FILE* stream) __overloadable
- __enable_if(!__unsafe_check_mul_overflow(size, count), "no overflow")
- __enable_if(__bos(buf) != __BIONIC_FORTIFY_UNKNOWN_SIZE &&
- size * count > __bos(buf), "size * count is too large")
- __errorattr("size * count is too large");
-
-__BIONIC_FORTIFY_INLINE
-size_t fread(void* const __pass_object_size0 buf, size_t size,
- size_t count, FILE* stream) __overloadable {
+size_t fread(void* const __pass_object_size0 buf, size_t size, size_t count, FILE* stream)
+ __overloadable
+ __clang_error_if(__unsafe_check_mul_overflow(size, count),
+ "in call to 'fread', size * count overflows")
+ __clang_error_if(__bos(buf) != __BIONIC_FORTIFY_UNKNOWN_SIZE && size * count > __bos(buf),
+ "in call to 'fread', size * count is too large for the given buffer") {
size_t bos = __bos0(buf);
if (bos == __BIONIC_FORTIFY_UNKNOWN_SIZE) {
return __call_bypassing_fortify(fread)(buf, size, count, stream);
}
-
return __fread_chk(buf, size, count, stream, bos);
}
-size_t fwrite(const void* buf, size_t size, size_t count, FILE* stream) __overloadable
- __enable_if(__unsafe_check_mul_overflow(size, count),
- "size * count overflows")
- __errorattr("size * count overflows");
-
-size_t fwrite(const void* buf, size_t size, size_t count, FILE* stream) __overloadable
- __enable_if(!__unsafe_check_mul_overflow(size, count), "no overflow")
- __enable_if(__bos(buf) != __BIONIC_FORTIFY_UNKNOWN_SIZE &&
- size * count > __bos(buf), "size * count is too large")
- __errorattr("size * count is too large");
-
__BIONIC_FORTIFY_INLINE
size_t fwrite(const void* const __pass_object_size0 buf, size_t size, size_t count, FILE* stream)
- __overloadable {
+ __overloadable
+ __clang_error_if(__unsafe_check_mul_overflow(size, count),
+ "in call to 'fwrite', size * count overflows")
+ __clang_error_if(__bos(buf) != __BIONIC_FORTIFY_UNKNOWN_SIZE && size * count > __bos(buf),
+ "in call to 'fwrite', size * count is too large for the given buffer") {
size_t bos = __bos0(buf);
if (bos == __BIONIC_FORTIFY_UNKNOWN_SIZE) {
@@ -143,19 +127,12 @@
#endif /* __ANDROID_API__ >= __ANDROID_API_N__ */
#if __ANDROID_API__ >= __ANDROID_API_J_MR1__
-__BIONIC_ERROR_FUNCTION_VISIBILITY
-char *fgets(char* dest, int size, FILE* stream) __overloadable
- __enable_if(size < 0, "size is negative")
- __errorattr("size is negative");
-
-__BIONIC_ERROR_FUNCTION_VISIBILITY
-char *fgets(char* dest, int size, FILE* stream) __overloadable
- __enable_if(size >= 0 && size > __bos(dest),
- "size is larger than the destination buffer")
- __errorattr("size is larger than the destination buffer");
-
__BIONIC_FORTIFY_INLINE
-char *fgets(char* const __pass_object_size dest, int size, FILE* stream) __overloadable {
+char* fgets(char* const __pass_object_size dest, int size, FILE* stream)
+ __overloadable
+ __clang_error_if(size < 0, "in call to 'fgets', size should not be negative")
+ __clang_error_if(size > __bos(dest),
+ "in call to 'fgets', size is larger than the destination buffer") {
size_t bos = __bos(dest);
if (bos == __BIONIC_FORTIFY_UNKNOWN_SIZE) {
diff --git a/libc/include/bits/fortify/stdlib.h b/libc/include/bits/fortify/stdlib.h
index bda1d45..cf4b7ea 100644
--- a/libc/include/bits/fortify/stdlib.h
+++ b/libc/include/bits/fortify/stdlib.h
@@ -32,22 +32,16 @@
#if defined(__BIONIC_FORTIFY)
#define __realpath_buf_too_small_str \
- "realpath output parameter must be NULL or a >= PATH_MAX bytes buffer"
+ "'realpath' output parameter must be NULL or a pointer to a buffer with >= PATH_MAX bytes"
/* PATH_MAX is unavailable without polluting the namespace, but it's always 4096 on Linux */
#define __PATH_MAX 4096
#if defined(__clang__)
-
-__BIONIC_ERROR_FUNCTION_VISIBILITY
-char* realpath(const char* path, char* resolved) __overloadable
- __enable_if(__bos(resolved) != __BIONIC_FORTIFY_UNKNOWN_SIZE &&
- __bos(resolved) < __PATH_MAX, __realpath_buf_too_small_str)
- __errorattr(__realpath_buf_too_small_str);
-
-/* No need for a FORTIFY version; the only things we can catch are at
- * compile-time.
- */
+char* realpath(const char* path, char* resolved)
+ __clang_error_if(__bos(resolved) != __BIONIC_FORTIFY_UNKNOWN_SIZE &&
+ __bos(resolved) < __PATH_MAX, __realpath_buf_too_small_str);
+/* No need for a definition; the only issues we can catch are at compile-time. */
#else /* defined(__clang__) */
diff --git a/libc/include/sched.h b/libc/include/sched.h
index d407202..b8ef44b 100644
--- a/libc/include/sched.h
+++ b/libc/include/sched.h
@@ -25,6 +25,7 @@
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
+
#ifndef _SCHED_H_
#define _SCHED_H_
@@ -41,22 +42,22 @@
int sched_priority;
};
-int sched_setscheduler(pid_t, int, const struct sched_param*);
-int sched_getscheduler(pid_t);
+int sched_setscheduler(pid_t pid, int policy, const struct sched_param* param);
+int sched_getscheduler(pid_t pid);
int sched_yield(void);
-int sched_get_priority_max(int);
-int sched_get_priority_min(int);
-int sched_setparam(pid_t, const struct sched_param*);
-int sched_getparam(pid_t, struct sched_param*);
-int sched_rr_get_interval(pid_t, struct timespec*);
+int sched_get_priority_max(int policy);
+int sched_get_priority_min(int policy);
+int sched_setparam(pid_t pid, const struct sched_param* param);
+int sched_getparam(pid_t pid, struct sched_param* param);
+int sched_rr_get_interval(pid_t pid, struct timespec* quantum);
#if defined(__USE_GNU)
-int clone(int (*)(void*), void*, int, void*, ...) __INTRODUCED_IN_ARM(9)
- __INTRODUCED_IN_MIPS(12) __INTRODUCED_IN_X86(17);
-int unshare(int) __INTRODUCED_IN(17);
+int clone(int (*fn)(void*), void* child_stack, int flags, void* arg, ...)
+ __INTRODUCED_IN_ARM(9) __INTRODUCED_IN_MIPS(12) __INTRODUCED_IN_X86(17);
+int unshare(int flags) __INTRODUCED_IN(17);
int sched_getcpu(void) __INTRODUCED_IN(12);
-int setns(int, int) __INTRODUCED_IN(21);
+int setns(int fd, int ns_type) __INTRODUCED_IN(21);
#ifdef __LP64__
#define CPU_SETSIZE 1024
diff --git a/libc/include/stdio_ext.h b/libc/include/stdio_ext.h
index fdf6772..3c5abc3 100644
--- a/libc/include/stdio_ext.h
+++ b/libc/include/stdio_ext.h
@@ -40,7 +40,9 @@
size_t __fbufsize(FILE*) __INTRODUCED_IN(23);
int __freadable(FILE*) __INTRODUCED_IN(23);
+int __freading(FILE*) __INTRODUCED_IN_FUTURE;
int __fwritable(FILE*) __INTRODUCED_IN(23);
+int __fwriting(FILE*) __INTRODUCED_IN_FUTURE;
int __flbf(FILE*) __INTRODUCED_IN(23);
void __fpurge(FILE*) __INTRODUCED_IN(23);
size_t __fpending(FILE*) __INTRODUCED_IN(23);
diff --git a/libc/include/stdlib.h b/libc/include/stdlib.h
index 55e0fa2..13c9d37 100644
--- a/libc/include/stdlib.h
+++ b/libc/include/stdlib.h
@@ -83,8 +83,7 @@
long atol(const char*) __attribute_pure__;
long long atoll(const char*) __attribute_pure__;
-char * realpath(const char *path, char *resolved) __overloadable
- __RENAME_CLANG(realpath);
+char* realpath(const char* path, char* resolved);
int system(const char *string);
void* bsearch(const void* key, const void* base0, size_t nmemb, size_t size,
diff --git a/libc/libc.arm.map b/libc/libc.arm.map
index d79e36b..06856c8 100644
--- a/libc/libc.arm.map
+++ b/libc/libc.arm.map
@@ -1320,6 +1320,8 @@
LIBC_P {
global:
+ __freading; # future
+ __fwriting; # future
getlogin_r; # future
} LIBC_O;
diff --git a/libc/libc.arm64.map b/libc/libc.arm64.map
index ed1e82c..50e4390 100644
--- a/libc/libc.arm64.map
+++ b/libc/libc.arm64.map
@@ -1240,6 +1240,8 @@
LIBC_P {
global:
+ __freading; # future
+ __fwriting; # future
getlogin_r; # future
} LIBC_O;
diff --git a/libc/libc.map.txt b/libc/libc.map.txt
index af8e550..3e4d36e 100644
--- a/libc/libc.map.txt
+++ b/libc/libc.map.txt
@@ -1345,6 +1345,8 @@
LIBC_P {
global:
+ __freading; # future
+ __fwriting; # future
getlogin_r; # future
} LIBC_O;
diff --git a/libc/libc.mips.map b/libc/libc.mips.map
index 90a65e0..3fa7c9d 100644
--- a/libc/libc.mips.map
+++ b/libc/libc.mips.map
@@ -1304,6 +1304,8 @@
LIBC_P {
global:
+ __freading; # future
+ __fwriting; # future
getlogin_r; # future
} LIBC_O;
diff --git a/libc/libc.mips64.map b/libc/libc.mips64.map
index ed1e82c..50e4390 100644
--- a/libc/libc.mips64.map
+++ b/libc/libc.mips64.map
@@ -1240,6 +1240,8 @@
LIBC_P {
global:
+ __freading; # future
+ __fwriting; # future
getlogin_r; # future
} LIBC_O;
diff --git a/libc/libc.x86.map b/libc/libc.x86.map
index 8217562..bd74fbe 100644
--- a/libc/libc.x86.map
+++ b/libc/libc.x86.map
@@ -1302,6 +1302,8 @@
LIBC_P {
global:
+ __freading; # future
+ __fwriting; # future
getlogin_r; # future
} LIBC_O;
diff --git a/libc/libc.x86_64.map b/libc/libc.x86_64.map
index ed1e82c..50e4390 100644
--- a/libc/libc.x86_64.map
+++ b/libc/libc.x86_64.map
@@ -1240,6 +1240,8 @@
LIBC_P {
global:
+ __freading; # future
+ __fwriting; # future
getlogin_r; # future
} LIBC_O;
diff --git a/libc/stdio/local.h b/libc/stdio/local.h
index 6fb2c0d..bf6a8f8 100644
--- a/libc/stdio/local.h
+++ b/libc/stdio/local.h
@@ -127,10 +127,11 @@
// Values for `__sFILE::_flags`.
#define __SLBF 0x0001 // Line buffered.
#define __SNBF 0x0002 // Unbuffered.
-// RD and WR are never simultaneously asserted: use _SRW instead.
-#define __SRD 0x0004 // OK to read.
-#define __SWR 0x0008 // OK to write.
-#define __SRW 0x0010 // Open for reading & writing.
+// __SRD and __SWR are mutually exclusive because they indicate what we did last.
+// If you want to know whether we were opened read-write, check __SRW instead.
+#define __SRD 0x0004 // Last operation was read.
+#define __SWR 0x0008 // Last operation was write.
+#define __SRW 0x0010 // Was opened for reading & writing.
#define __SEOF 0x0020 // Found EOF.
#define __SERR 0x0040 // Found error.
#define __SMBF 0x0080 // `_buf` is from malloc.
diff --git a/libc/stdio/stdio_ext.cpp b/libc/stdio/stdio_ext.cpp
index ebc705c..8cf4f4b 100644
--- a/libc/stdio/stdio_ext.cpp
+++ b/libc/stdio/stdio_ext.cpp
@@ -39,17 +39,13 @@
return fp->_bf._size;
}
-/* For a _SRW stream, we don't know whether we last read or wrote.
int __freading(FILE* fp) {
- return (fp->_flags & _SRD) != 0 || ...;
+ return (fp->_flags & __SRD) != 0;
}
-*/
-/* For a _SRW stream, we don't know whether we last read or wrote.
-int __fwriting(FILE*) {
- return (fp->_flags & _SWR) != 0 || ...;
+int __fwriting(FILE* fp) {
+ return (fp->_flags & __SWR) != 0;
}
-*/
int __freadable(FILE* fp) {
return (fp->_flags & (__SRD|__SRW)) != 0;
diff --git a/libc/upstream-freebsd/lib/libc/stdlib/realpath.c b/libc/upstream-freebsd/lib/libc/stdlib/realpath.c
index 914ecc9..c4bd953 100644
--- a/libc/upstream-freebsd/lib/libc/stdlib/realpath.c
+++ b/libc/upstream-freebsd/lib/libc/stdlib/realpath.c
@@ -48,7 +48,7 @@
* in which case the path which caused trouble is left in (resolved).
*/
char *
-realpath(const char * __restrict path, char * __restrict resolved) __overloadable
+realpath(const char * __restrict path, char * __restrict resolved)
{
struct stat sb;
char *p, *q, *s;
diff --git a/linker/Android.bp b/linker/Android.bp
index efd91ac..fda7eb5 100644
--- a/linker/Android.bp
+++ b/linker/Android.bp
@@ -101,6 +101,12 @@
"-Werror",
],
+ product_variables: {
+ debuggable: {
+ cppflags: ["-DUSE_LD_CONFIG_FILE"],
+ },
+ },
+
cppflags: ["-Wold-style-cast"],
// we are going to link libc++_static manually because
diff --git a/linker/linker.cpp b/linker/linker.cpp
index 8e7a141..a212624 100644
--- a/linker/linker.cpp
+++ b/linker/linker.cpp
@@ -558,9 +558,10 @@
static LoadTask* create(const char* name,
soinfo* needed_by,
+ android_namespace_t* start_from,
std::unordered_map<const soinfo*, ElfReader>* readers_map) {
LoadTask* ptr = TypeBasedAllocator<LoadTask>::alloc();
- return new (ptr) LoadTask(name, needed_by, readers_map);
+ return new (ptr) LoadTask(name, needed_by, start_from, readers_map);
}
const char* get_name() const {
@@ -612,6 +613,11 @@
is_dt_needed_ = is_dt_needed;
}
+ // returns the namespace from where we need to start loading this.
+ const android_namespace_t* get_start_from() const {
+ return start_from_;
+ }
+
const ElfReader& get_elf_reader() const {
CHECK(si_ != nullptr);
return (*elf_readers_map_)[si_];
@@ -650,10 +656,11 @@
private:
LoadTask(const char* name,
soinfo* needed_by,
+ android_namespace_t* start_from,
std::unordered_map<const soinfo*, ElfReader>* readers_map)
: name_(name), needed_by_(needed_by), si_(nullptr),
fd_(-1), close_fd_(false), file_offset_(0), elf_readers_map_(readers_map),
- is_dt_needed_(false) {}
+ is_dt_needed_(false), start_from_(start_from) {}
~LoadTask() {
if (fd_ != -1 && close_fd_) {
@@ -672,6 +679,7 @@
// TODO(dimitry): needed by workaround for http://b/26394120 (the grey-list)
bool is_dt_needed_;
// END OF WORKAROUND
+ const android_namespace_t* const start_from_;
DISALLOW_IMPLICIT_CONSTRUCTORS(LoadTask);
};
@@ -1041,7 +1049,7 @@
ZipArchiveCache* zip_archive_cache,
const char* name, soinfo *needed_by,
off64_t* file_offset, std::string* realpath) {
- TRACE("[ opening %s ]", name);
+ TRACE("[ opening %s at namespace %s]", name, ns->get_name());
// If the name contains a slash, we should attempt to open it directly and not search the paths.
if (strchr(name, '/') != nullptr) {
@@ -1273,7 +1281,7 @@
}
for_each_dt_needed(task->get_elf_reader(), [&](const char* name) {
- load_tasks->push_back(LoadTask::create(name, si, task->get_readers_map()));
+ load_tasks->push_back(LoadTask::create(name, si, ns, task->get_readers_map()));
});
return true;
@@ -1368,8 +1376,7 @@
}
static bool find_library_in_linked_namespace(const android_namespace_link_t& namespace_link,
- LoadTask* task,
- int rtld_flags) {
+ LoadTask* task) {
android_namespace_t* ns = namespace_link.linked_namespace();
soinfo* candidate;
@@ -1394,29 +1401,10 @@
return true;
}
- // try to load the library - once namespace boundary is crossed
- // we need to load a library within separate load_group
- // to avoid using symbols from foreign namespace while.
- //
- // All symbols during relocation should be resolved within a
- // namespace to preserve library locality to a namespace.
- const char* name = task->get_name();
- if (find_libraries(ns,
- task->get_needed_by(),
- &name,
- 1,
- &candidate,
- nullptr /* ld_preloads */,
- 0 /* ld_preload_count*/,
- rtld_flags,
- nullptr /* extinfo*/,
- false /* add_as_children */,
- false /* search_linked_namespaces */)) {
- task->set_soinfo(candidate);
- return true;
- }
-
- return false;
+ // returning true with empty soinfo means that the library is okay to be
+ // loaded in the namespace buy has not yet been loaded there before.
+ task->set_soinfo(nullptr);
+ return true;
}
static bool find_library_internal(android_namespace_t* ns,
@@ -1445,9 +1433,24 @@
// if a library was not found - look into linked namespaces
for (auto& linked_namespace : ns->linked_namespaces()) {
if (find_library_in_linked_namespace(linked_namespace,
- task,
- rtld_flags)) {
- return true;
+ task)) {
+ if (task->get_soinfo() == nullptr) {
+ // try to load the library - once namespace boundary is crossed
+ // we need to load a library within separate load_group
+ // to avoid using symbols from foreign namespace while.
+ //
+ // However, actual linking is deferred until when the global group
+ // is fully identified and is applied to all namespaces.
+ // Otherwise, the libs in the linked namespace won't get symbols from
+ // the global group.
+ if (load_library(linked_namespace.linked_namespace(), task, zip_archive_cache, load_tasks, rtld_flags, false)) {
+ return true;
+ }
+ // lib was not found in the namespace. Try next linked namespace.
+ } else {
+ // lib is already loaded
+ return true;
+ }
}
}
}
@@ -1458,44 +1461,6 @@
static void soinfo_unload(soinfo* si);
static void soinfo_unload(soinfo* soinfos[], size_t count);
-// TODO: this is slightly unusual way to construct
-// the global group for relocation. Not every RTLD_GLOBAL
-// library is included in this group for backwards-compatibility
-// reasons.
-//
-// This group consists of the main executable, LD_PRELOADs
-// and libraries with the DF_1_GLOBAL flag set.
-static soinfo_list_t make_global_group(android_namespace_t* ns) {
- soinfo_list_t global_group;
- ns->soinfo_list().for_each([&](soinfo* si) {
- if ((si->get_dt_flags_1() & DF_1_GLOBAL) != 0) {
- global_group.push_back(si);
- }
- });
-
- return global_group;
-}
-
-// This function provides a list of libraries to be shared
-// by the namespace. For the default namespace this is the global
-// group (see make_global_group). For all others this is a group
-// of RTLD_GLOBAL libraries (which includes the global group from
-// the default namespace).
-static soinfo_list_t get_shared_group(android_namespace_t* ns) {
- if (ns == &g_default_namespace) {
- return make_global_group(ns);
- }
-
- soinfo_list_t shared_group;
- ns->soinfo_list().for_each([&](soinfo* si) {
- if ((si->get_rtld_flags() & RTLD_GLOBAL) != 0) {
- shared_group.push_back(si);
- }
- });
-
- return shared_group;
-}
-
static void shuffle(std::vector<LoadTask*>* v) {
for (size_t i = 0, size = v->size(); i < size; ++i) {
size_t n = size - i;
@@ -1518,19 +1483,17 @@
int rtld_flags,
const android_dlextinfo* extinfo,
bool add_as_children,
- bool search_linked_namespaces) {
+ bool search_linked_namespaces,
+ std::unordered_map<const soinfo*, ElfReader>& readers_map,
+ std::vector<android_namespace_t*>* namespaces) {
// Step 0: prepare.
LoadTaskList load_tasks;
- std::unordered_map<const soinfo*, ElfReader> readers_map;
for (size_t i = 0; i < library_names_count; ++i) {
const char* name = library_names[i];
- load_tasks.push_back(LoadTask::create(name, start_with, &readers_map));
+ load_tasks.push_back(LoadTask::create(name, start_with, ns, &readers_map));
}
- // Construct global_group.
- soinfo_list_t global_group = make_global_group(ns);
-
// If soinfos array is null allocate one on stack.
// The array is needed in case of failure; for example
// when library_names[] = {libone.so, libtwo.so} and libone.so
@@ -1570,7 +1533,12 @@
task->set_extinfo(is_dt_needed ? nullptr : extinfo);
task->set_dt_needed(is_dt_needed);
- if (!find_library_internal(ns,
+ // try to find the load.
+ // Note: start from the namespace that is stored in the LoadTask. This namespace
+ // is different from the current namespace when the LoadTask is for a transitive
+ // dependency and the lib that created the LoadTask is not found in the
+ // current namespace but in one of the linked namespace.
+ if (!find_library_internal(const_cast<android_namespace_t*>(task->get_start_from()),
task,
&zip_archive_cache,
&load_tasks,
@@ -1629,18 +1597,61 @@
}
}
- // Step 4: Add LD_PRELOADed libraries to the global group for
- // future runs. There is no need to explicitly add them to
- // the global group for this run because they are going to
- // appear in the local group in the correct order.
+ // Step 4: Construct the global group. Note: DF_1_GLOBAL bit of a library is
+ // determined at step 3.
+
+ // Step 4-1: DF_1_GLOBAL bit is force set for LD_PRELOADed libs because they
+ // must be added to the global group
if (ld_preloads != nullptr) {
for (auto&& si : *ld_preloads) {
si->set_dt_flags_1(si->get_dt_flags_1() | DF_1_GLOBAL);
}
}
+ // Step 4-2: Gather all DF_1_GLOBAL libs which were newly loaded during this
+ // run. These will be the new member of the global group
+ soinfo_list_t new_global_group_members;
+ for (auto&& task : load_tasks) {
+ soinfo* si = task->get_soinfo();
+ if (!si->is_linked() && (si->get_dt_flags_1() & DF_1_GLOBAL) != 0) {
+ new_global_group_members.push_back(si);
+ }
+ }
- // Step 5: link libraries.
+ // Step 4-3: Add the new global group members to all the linked namespaces
+ for (auto si : new_global_group_members) {
+ for (auto linked_ns : *namespaces) {
+ if (si->get_primary_namespace() != linked_ns) {
+ linked_ns->add_soinfo(si);
+ si->add_secondary_namespace(linked_ns);
+ }
+ }
+ }
+
+ // Step 5: link libraries that are not destined to this namespace.
+ // Do this by recursively calling find_libraries on the namespace where the lib
+ // was found during Step 1.
+ for (auto&& task : load_tasks) {
+ soinfo* si = task->get_soinfo();
+ if (si->get_primary_namespace() != ns) {
+ const char* name = task->get_name();
+ if (find_libraries(si->get_primary_namespace(), task->get_needed_by(), &name, 1,
+ nullptr /* soinfos */, nullptr /* ld_preloads */, 0 /* ld_preload_count */,
+ rtld_flags, nullptr /* extinfo */, false /* add_as_children */,
+ false /* search_linked_namespaces */, readers_map, namespaces)) {
+ // If this lib is directly needed by one of the libs in this namespace,
+ // then increment the count
+ soinfo* needed_by = task->get_needed_by();
+ if (needed_by != nullptr && needed_by->get_primary_namespace() == ns && si->is_linked()) {
+ si->increment_ref_count();
+ }
+ } else {
+ return false;
+ }
+ }
+ }
+
+ // Step 6: link libraries in this namespace
soinfo_list_t local_group;
walk_dependencies_tree(
(start_with != nullptr && add_as_children) ? &start_with : soinfos,
@@ -1654,6 +1665,7 @@
}
});
+ soinfo_list_t global_group = ns->get_global_group();
bool linked = local_group.visit([&](soinfo* si) {
if (!si->is_linked()) {
if (!si->link_image(global_group, local_group, extinfo) ||
@@ -1684,6 +1696,9 @@
soinfo* needed_by) {
soinfo* si;
+ // readers_map is shared across recursive calls to find_libraries.
+ // However, the map is not shared across different threads.
+ std::unordered_map<const soinfo*, ElfReader> readers_map;
if (name == nullptr) {
si = solist_get_somain();
} else if (!find_libraries(ns,
@@ -1696,7 +1711,8 @@
rtld_flags,
extinfo,
false /* add_as_children */,
- true /* search_linked_namespaces */)) {
+ true /* search_linked_namespaces */,
+ readers_map)) {
return nullptr;
}
@@ -2208,7 +2224,7 @@
}
} else {
// If not shared - copy only the shared group
- add_soinfos_to_namespace(get_shared_group(parent_namespace), ns);
+ add_soinfos_to_namespace(parent_namespace->get_shared_group(), ns);
}
ns->set_ld_library_paths(std::move(ld_library_paths));
@@ -3413,7 +3429,7 @@
return true;
}
-static void init_default_namespace_no_config(bool is_asan) {
+static std::vector<android_namespace_t*> init_default_namespace_no_config(bool is_asan) {
g_default_namespace.set_isolated(false);
auto default_ld_paths = is_asan ? kAsanDefaultLdPaths : kDefaultLdPaths;
@@ -3428,9 +3444,13 @@
}
g_default_namespace.set_default_library_paths(std::move(ld_default_paths));
+
+ std::vector<android_namespace_t*> namespaces;
+ namespaces.push_back(&g_default_namespace);
+ return namespaces;
}
-void init_default_namespace(const char* executable_path) {
+std::vector<android_namespace_t*> init_default_namespaces(const char* executable_path) {
g_default_namespace.set_name("(default)");
soinfo* somain = solist_get_somain();
@@ -3447,14 +3467,24 @@
std::string error_msg;
- if (!Config::read_binary_config(kLdConfigFilePath,
+ const char* config_file = kLdConfigFilePath;
+#ifdef USE_LD_CONFIG_FILE
+ // This is a debugging/testing only feature. Must not be available on
+ // production builds.
+ const char* ld_config_file = getenv("LD_CONFIG_FILE");
+ if (ld_config_file != nullptr && file_exists(ld_config_file)) {
+ config_file = ld_config_file;
+ }
+#endif
+
+ if (!Config::read_binary_config(config_file,
executable_path,
g_is_asan,
&config,
&error_msg)) {
if (!error_msg.empty()) {
DL_WARN("error reading config file \"%s\" for \"%s\" (will use default configuration): %s",
- kLdConfigFilePath,
+ config_file,
executable_path,
error_msg.c_str());
}
@@ -3462,8 +3492,7 @@
}
if (config == nullptr) {
- init_default_namespace_no_config(g_is_asan);
- return;
+ return init_default_namespace_no_config(g_is_asan);
}
const auto& namespace_configs = config->namespace_configs();
@@ -3514,10 +3543,17 @@
soinfo* ld_android_so = solist_get_head();
for (auto it : namespaces) {
it.second->add_soinfo(ld_android_so);
- // TODO (dimitry): somain and ld_preloads should probably be added to all of these namespaces too?
+ // somain and ld_preloads are added to these namespaces after LD_PRELOAD libs are linked
}
set_application_target_sdk_version(config->target_sdk_version());
+
+ std::vector<android_namespace_t*> created_namespaces;
+ created_namespaces.reserve(namespaces.size());
+ for (auto kv : namespaces) {
+ created_namespaces.push_back(kv.second);
+ }
+ return created_namespaces;
}
// This function finds a namespace exported in ld.config.txt by its name.
diff --git a/linker/linker_main.cpp b/linker/linker_main.cpp
index bcb2281..54593e4 100644
--- a/linker/linker_main.cpp
+++ b/linker/linker_main.cpp
@@ -341,12 +341,19 @@
somain = si;
- init_default_namespace(executable_path);
+ std::vector<android_namespace_t*> namespaces = init_default_namespaces(executable_path);
if (!si->prelink_image()) __linker_cannot_link(g_argv[0]);
// add somain to global group
si->set_dt_flags_1(si->get_dt_flags_1() | DF_1_GLOBAL);
+ // ... and add it to all other linked namespaces
+ for (auto linked_ns : namespaces) {
+ if (linked_ns != &g_default_namespace) {
+ linked_ns->add_soinfo(somain);
+ somain->add_secondary_namespace(linked_ns);
+ }
+ }
// Load ld_preloads and dependencies.
std::vector<const char*> needed_library_name_list;
@@ -364,6 +371,9 @@
const char** needed_library_names = &needed_library_name_list[0];
size_t needed_libraries_count = needed_library_name_list.size();
+ // readers_map is shared across recursive calls to find_libraries so that we
+ // don't need to re-load elf headers.
+ std::unordered_map<const soinfo*, ElfReader> readers_map;
if (needed_libraries_count > 0 &&
!find_libraries(&g_default_namespace,
si,
@@ -375,7 +385,9 @@
RTLD_GLOBAL,
nullptr,
true /* add_as_children */,
- true /* search_linked_namespaces */)) {
+ true /* search_linked_namespaces */,
+ readers_map,
+ &namespaces)) {
__linker_cannot_link(g_argv[0]);
} else if (needed_libraries_count == 0) {
if (!si->link_image(g_empty_list, soinfo_list_t::make_list(si), nullptr)) {
diff --git a/linker/linker_main.h b/linker/linker_main.h
index 8f3f07c..2cf30c2 100644
--- a/linker/linker_main.h
+++ b/linker/linker_main.h
@@ -31,6 +31,9 @@
#include <android/dlext.h>
+#include <unordered_map>
+#include <vector>
+
#include "linker_namespaces.h"
#include "linker_soinfo.h"
@@ -44,7 +47,9 @@
static size_t ref_count_;
};
-void init_default_namespace(const char* executable_path);
+class ElfReader;
+
+std::vector<android_namespace_t*> init_default_namespaces(const char* executable_path);
soinfo* soinfo_alloc(android_namespace_t* ns, const char* name,
struct stat* file_stat, off64_t file_offset,
uint32_t rtld_flags);
@@ -59,7 +64,9 @@
int rtld_flags,
const android_dlextinfo* extinfo,
bool add_as_children,
- bool search_linked_namespaces);
+ bool search_linked_namespaces,
+ std::unordered_map<const soinfo*, ElfReader>& readers_map,
+ std::vector<android_namespace_t*>* namespaces = nullptr);
void solist_add_soinfo(soinfo* si);
bool solist_remove_soinfo(soinfo* si);
diff --git a/linker/linker_namespaces.cpp b/linker/linker_namespaces.cpp
index 3c86f99..9fdf0b5 100644
--- a/linker/linker_namespaces.cpp
+++ b/linker/linker_namespaces.cpp
@@ -31,6 +31,8 @@
#include "linker_soinfo.h"
#include "linker_utils.h"
+#include <dlfcn.h>
+
bool android_namespace_t::is_accessible(const std::string& file) {
if (!is_isolated_) {
return true;
@@ -86,3 +88,41 @@
return !is_accessible_ftor(si);
});
}
+
+// TODO: this is slightly unusual way to construct
+// the global group for relocation. Not every RTLD_GLOBAL
+// library is included in this group for backwards-compatibility
+// reasons.
+//
+// This group consists of the main executable, LD_PRELOADs
+// and libraries with the DF_1_GLOBAL flag set.
+soinfo_list_t android_namespace_t::get_global_group() {
+ soinfo_list_t global_group;
+ soinfo_list().for_each([&](soinfo* si) {
+ if ((si->get_dt_flags_1() & DF_1_GLOBAL) != 0) {
+ global_group.push_back(si);
+ }
+ });
+
+ return global_group;
+}
+
+// This function provides a list of libraries to be shared
+// by the namespace. For the default namespace this is the global
+// group (see get_global_group). For all others this is a group
+// of RTLD_GLOBAL libraries (which includes the global group from
+// the default namespace).
+soinfo_list_t android_namespace_t::get_shared_group() {
+ if (this == &g_default_namespace) {
+ return get_global_group();
+ }
+
+ soinfo_list_t shared_group;
+ soinfo_list().for_each([&](soinfo* si) {
+ if ((si->get_rtld_flags() & RTLD_GLOBAL) != 0) {
+ shared_group.push_back(si);
+ }
+ });
+
+ return shared_group;
+}
diff --git a/linker/linker_namespaces.h b/linker/linker_namespaces.h
index 1099b6b..16906d6 100644
--- a/linker/linker_namespaces.h
+++ b/linker/linker_namespaces.h
@@ -136,6 +136,9 @@
// or one of it's parent soinfos belongs to this namespace.
bool is_accessible(soinfo* si);
+ soinfo_list_t get_global_group();
+ soinfo_list_t get_shared_group();
+
private:
const char* name_;
bool is_isolated_;
diff --git a/linker/linker_phdr.cpp b/linker/linker_phdr.cpp
index 42c29c8..a9873c4 100644
--- a/linker/linker_phdr.cpp
+++ b/linker/linker_phdr.cpp
@@ -147,8 +147,9 @@
}
bool ElfReader::Read(const char* name, int fd, off64_t file_offset, off64_t file_size) {
- CHECK(!did_read_);
- CHECK(!did_load_);
+ if (did_read_) {
+ return true;
+ }
name_ = name;
fd_ = fd;
file_offset_ = file_offset;
@@ -167,7 +168,9 @@
bool ElfReader::Load(const android_dlextinfo* extinfo) {
CHECK(did_read_);
- CHECK(!did_load_);
+ if (did_load_) {
+ return true;
+ }
if (ReserveAddressSpace(extinfo) &&
LoadSegments() &&
FindPhdr()) {
diff --git a/tests/Android.bp b/tests/Android.bp
index 8f1c11f..084d393 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -298,7 +298,13 @@
"libLLVMSupport",
],
}
- }
+ },
+
+ product_variables: {
+ debuggable: {
+ cppflags: ["-DUSE_LD_CONFIG_FILE"],
+ },
+ },
}
// -----------------------------------------------------------------------------
@@ -592,6 +598,12 @@
sanitize: {
never: false,
},
+
+ product_variables: {
+ debuggable: {
+ cppflags: ["-DUSE_LD_CONFIG_FILE"],
+ },
+ },
}
subdirs = ["libs"]
diff --git a/tests/dl_test.cpp b/tests/dl_test.cpp
index aa8bd57..857640a 100644
--- a/tests/dl_test.cpp
+++ b/tests/dl_test.cpp
@@ -23,6 +23,8 @@
#include <stdint.h>
#include <string>
+#include <iostream>
+#include <fstream>
#include "gtest_globals.h"
#include "utils.h"
@@ -109,4 +111,131 @@
#endif
}
-// TODO: Add tests for LD_PRELOADs
+
+TEST(dl, exec_without_ld_preload) {
+#if defined(__BIONIC__)
+ std::string helper = get_testlib_root() +
+ "/ld_preload_test_helper/ld_preload_test_helper";
+ chmod(helper.c_str(), 0755);
+ ExecTestHelper eth;
+ eth.SetArgs({ helper.c_str(), nullptr });
+ eth.Run([&]() { execve(helper.c_str(), eth.GetArgs(), eth.GetEnv()); }, 0, "12345");
+#endif
+}
+
+TEST(dl, exec_with_ld_preload) {
+#if defined(__BIONIC__)
+ std::string helper = get_testlib_root() +
+ "/ld_preload_test_helper/ld_preload_test_helper";
+ std::string env = std::string("LD_PRELOAD=") + get_testlib_root() + "/ld_preload_test_helper_lib2.so";
+ chmod(helper.c_str(), 0755);
+ ExecTestHelper eth;
+ eth.SetArgs({ helper.c_str(), nullptr });
+ eth.SetEnv({ env.c_str(), nullptr });
+ // ld_preload_test_helper calls get_value_from_lib() and returns the value.
+ // The symbol is defined by two libs: ld_preload_test_helper_lib.so and
+ // ld_preloaded_lib.so. The former is DT_NEEDED and the latter is LD_PRELOADED
+ // via this execution. The main executable is linked to the LD_PRELOADED lib
+ // and the value given from the lib is returned.
+ eth.Run([&]() { execve(helper.c_str(), eth.GetArgs(), eth.GetEnv()); }, 0, "54321");
+#endif
+}
+
+
+// ld_config_test_helper must fail because it is depending on a lib which is not
+// in the search path
+//
+// Call sequence is...
+// _helper -- (get_value_from_lib()) -->
+// _lib1.so -- (get_value_from_another_lib()) -->
+// _lib2.so (returns 12345)
+// The two libs are in ns2/ subdir.
+TEST(dl, exec_without_ld_config_file) {
+#if defined(__BIONIC__)
+ std::string error_message = "CANNOT LINK EXECUTABLE \"" + get_testlib_root() + "/ld_config_test_helper/ld_config_test_helper\": library \"ld_config_test_helper_lib1.so\" not found\n";
+ std::string helper = get_testlib_root() +
+ "/ld_config_test_helper/ld_config_test_helper";
+ chmod(helper.c_str(), 0755);
+ ExecTestHelper eth;
+ eth.SetArgs({ helper.c_str(), nullptr });
+ eth.Run([&]() { execve(helper.c_str(), eth.GetArgs(), eth.GetEnv()); }, -6, error_message.c_str());
+#endif
+}
+
+#if defined(__BIONIC__)
+static void create_ld_config_file(std::string& config_file) {
+ std::ofstream fout(config_file.c_str(), std::ios::out);
+ fout << "dir.test = " << get_testlib_root() << "/ld_config_test_helper/" << std::endl
+ << "[test]" << std::endl
+ << "additional.namespaces = ns2" << std::endl
+ << "namespace.default.search.paths = " << get_testlib_root() << std::endl
+ << "namespace.default.links = ns2" << std::endl
+ << "namespace.default.link.ns2.shared_libs = libc.so:libm.so:libdl.so:ld_config_test_helper_lib1.so" << std::endl
+ << "namespace.ns2.search.paths = /system/${LIB}:" << get_testlib_root() << "/ns2" << std::endl;
+ fout.close();
+}
+#endif
+
+#ifdef USE_LD_CONFIG_FILE
+
+// _lib1.so and _lib2.so are now searchable by having another namespace 'ns2'
+// whose search paths include the 'ns2/' subdir.
+TEST(dl, exec_with_ld_config_file) {
+#if defined(__BIONIC__)
+ std::string helper = get_testlib_root() +
+ "/ld_config_test_helper/ld_config_test_helper";
+ std::string config_file = get_testlib_root() + "/ld.config.txt";
+ create_ld_config_file(config_file);
+ std::string env = std::string("LD_CONFIG_FILE=") + config_file;
+ chmod(helper.c_str(), 0755);
+ ExecTestHelper eth;
+ eth.SetArgs({ helper.c_str(), nullptr });
+ eth.SetEnv({ env.c_str(), nullptr });
+ eth.Run([&]() { execve(helper.c_str(), eth.GetArgs(), eth.GetEnv()); }, 0, "12345");
+#endif
+}
+
+// _lib3.so has same symbol as lib2.so but returns 54321. _lib3.so is
+// LD_PRELOADed. This test is to ensure LD_PRELOADed libs are available to
+// additional namespaces other than the default namespace.
+TEST(dl, exec_with_ld_config_file_with_ld_preload) {
+#if defined(__BIONIC__)
+ std::string helper = get_testlib_root() +
+ "/ld_config_test_helper/ld_config_test_helper";
+ std::string config_file = get_testlib_root() + "/ld.config.txt";
+ create_ld_config_file(config_file);
+ std::string env = std::string("LD_CONFIG_FILE=") + config_file;
+ std::string env2 = std::string("LD_PRELOAD=") + get_testlib_root() + "/ld_config_test_helper_lib3.so";
+ chmod(helper.c_str(), 0755);
+ ExecTestHelper eth;
+ eth.SetArgs({ helper.c_str(), nullptr });
+ eth.SetEnv({ env.c_str(), env2.c_str(), nullptr });
+ eth.Run([&]() { execve(helper.c_str(), eth.GetArgs(), eth.GetEnv()); }, 0, "54321");
+#endif
+}
+
+#endif // USE_LD_CONFIG_FILE
+
+// ensures that LD_CONFIG_FILE env var does not work for production builds.
+// The test input is the same as exec_with_ld_config_file, but it must fail in
+// this case.
+TEST(dl, disable_ld_config_file) {
+#if defined(__BIONIC__)
+ if (getuid() == 0) {
+ // when executed from the shell (e.g. not as part of CTS), skip the test.
+ // This test is only for CTS.
+ return;
+ }
+ std::string error_message = "CANNOT LINK EXECUTABLE \"" + get_testlib_root() + "/ld_config_test_helper/ld_config_test_helper\": library \"ld_config_test_helper_lib1.so\" not found\n";
+ std::string helper = get_testlib_root() +
+ "/ld_config_test_helper/ld_config_test_helper";
+ std::string config_file = get_testlib_root() + "/ld.config.txt";
+ create_ld_config_file(config_file);
+ std::string env = std::string("LD_CONFIG_FILE=") + config_file;
+ chmod(helper.c_str(), 0755);
+ ExecTestHelper eth;
+ eth.SetArgs({ helper.c_str(), nullptr });
+ eth.SetEnv({ env.c_str(), nullptr });
+ eth.Run([&]() { execve(helper.c_str(), eth.GetArgs(), eth.GetEnv()); }, -6, error_message.c_str());
+#endif
+}
diff --git a/tests/fortify_compilation_test.cpp b/tests/fortify_compilation_test.cpp
index 7def77e..db800a2 100644
--- a/tests/fortify_compilation_test.cpp
+++ b/tests/fortify_compilation_test.cpp
@@ -179,12 +179,12 @@
// NOLINTNEXTLINE(whitespace/line_length)
// GCC: error: call to '__fgets_too_small_error' declared with attribute error: fgets called with size less than zero
- // CLANG: error: call to unavailable function 'fgets': size is negative
+ // CLANG: error: in call to 'fgets', size should not be negative
fgets(buf, -1, stdin);
// NOLINTNEXTLINE(whitespace/line_length)
// GCC: error: call to '__fgets_too_big_error' declared with attribute error: fgets called with size bigger than buffer
- // CLANG: error: call to unavailable function 'fgets': size is larger than the destination buffer
+ // CLANG: error: in call to 'fgets', size is larger than the destination buffer
fgets(buf, 6, stdin);
}
@@ -193,15 +193,24 @@
sockaddr_in addr;
// NOLINTNEXTLINE(whitespace/line_length)
- // GCC: error: call to '__recvfrom_error' declared with attribute error: recvfrom called with size bigger than buffer
- // CLANG: error: call to unavailable function 'recvfrom': recvfrom called with size bigger than buffer
+ // GCC: error: call to '__recvfrom_error' declared with attribute error: 'recvfrom' called with size bigger than buffer
+ // CLANG: error: 'recvfrom' called with size bigger than buffer
recvfrom(0, buf, 6, 0, reinterpret_cast<sockaddr*>(&addr), NULL);
}
+void test_recv() {
+ char buf[4] = {0};
+
+ // NOLINTNEXTLINE(whitespace/line_length)
+ // GCC: error: call to '__recvfrom_error' declared with attribute error: 'recvfrom' called with size bigger than buffer
+ // CLANG: error: 'recv' called with size bigger than buffer
+ recv(0, buf, 6, 0);
+}
+
void test_umask() {
// NOLINTNEXTLINE(whitespace/line_length)
- // GCC: error: call to '__umask_invalid_mode' declared with attribute error: umask called with invalid mode
- // CLANG: error: call to unavailable function 'umask': umask called with invalid mode
+ // GCC: error: call to '__umask_invalid_mode' declared with attribute error: 'umask' called with invalid mode
+ // CLANG: error: 'umask' called with invalid mode
umask(01777);
}
@@ -216,7 +225,7 @@
void test_open() {
// NOLINTNEXTLINE(whitespace/line_length)
// GCC: error: call to '__creat_missing_mode' declared with attribute error: called with O_CREAT, but missing mode
- // CLANG: error: call to unavailable function 'open': called with O_CREAT, but missing mode
+ // CLANG: error: 'open' called with O_CREAT, but missing mode
open("/dev/null", O_CREAT);
// NOLINTNEXTLINE(whitespace/line_length)
@@ -229,7 +238,7 @@
pollfd fds[1];
// NOLINTNEXTLINE(whitespace/line_length)
// GCC: error: call to '__poll_too_small_error' declared with attribute error: poll: pollfd array smaller than fd count
- // CLANG: error: call to unavailable function 'poll': too many fds specified
+ // CLANG: error: in call to 'poll', fd_count is larger than the given buffer
poll(fds, 2, 0);
}
@@ -238,7 +247,7 @@
timespec timeout;
// NOLINTNEXTLINE(whitespace/line_length)
// GCC: error: call to '__ppoll_too_small_error' declared with attribute error: ppoll: pollfd array smaller than fd count
- // CLANG: error: call to unavailable function 'ppoll': too many fds specified
+ // CLANG: error: in call to 'ppoll', fd_count is larger than the given buffer
ppoll(fds, 2, &timeout, NULL);
}
@@ -246,7 +255,7 @@
char buf[4];
// NOLINTNEXTLINE(whitespace/line_length)
// GCC: error: call to '__fread_overflow' declared with attribute error: fread called with overflowing size * count
- // CLANG: error: call to unavailable function 'fread': size * count overflows
+ // CLANG: error: in call to 'fread', size * count overflows
fread(buf, 2, (size_t)-1, stdin);
}
@@ -254,7 +263,8 @@
char buf[4];
// NOLINTNEXTLINE(whitespace/line_length)
// GCC: error: call to '__fread_too_big_error' declared with attribute error: fread called with size * count bigger than buffer
- // CLANG: error: call to unavailable function 'fread': size * count is too large
+ // NOLINTNEXTLINE(whitespace/line_length)
+ // CLANG: error: in call to 'fread', size * count is too large for the given buffer
fread(buf, 1, 5, stdin);
}
@@ -262,7 +272,7 @@
char buf[4] = {0};
// NOLINTNEXTLINE(whitespace/line_length)
// GCC: error: call to '__fwrite_overflow' declared with attribute error: fwrite called with overflowing size * count
- // CLANG: error: call to unavailable function 'fwrite': size * count overflows
+ // CLANG: error: in call to 'fwrite', size * count overflows
fwrite(buf, 2, (size_t)-1, stdout);
}
@@ -270,7 +280,8 @@
char buf[4] = {0};
// NOLINTNEXTLINE(whitespace/line_length)
// GCC: error: call to '__fwrite_too_big_error' declared with attribute error: fwrite called with size * count bigger than buffer
- // CLANG: error: call to unavailable function 'fwrite': size * count is too large
+ // NOLINTNEXTLINE(whitespace/line_length)
+ // CLANG: error: in call to 'fwrite', size * count is too large for the given buffer
fwrite(buf, 1, 5, stdout);
}
@@ -326,8 +337,8 @@
sockaddr_in addr;
// NOLINTNEXTLINE(whitespace/line_length)
- // GCC: error: call to '__sendto_error' declared with attribute error: sendto called with size bigger than buffer
- // CLANG: error: call to unavailable function 'sendto': sendto called with size bigger than buffer
+ // GCC: error: call to '__sendto_error' declared with attribute error: 'sendto' called with size bigger than buffer
+ // CLANG: error: 'sendto' called with size bigger than buffer
sendto(0, buf, 6, 0, reinterpret_cast<sockaddr*>(&addr), sizeof(sockaddr_in));
}
@@ -335,7 +346,22 @@
char buf[4] = {0};
// NOLINTNEXTLINE(whitespace/line_length)
- // GCC: error: call to '__sendto_error' declared with attribute error: sendto called with size bigger than buffer
- // CLANG: error: call to unavailable function 'send': send called with size bigger than buffer
+ // GCC: error: call to '__sendto_error' declared with attribute error: 'sendto' called with size bigger than buffer
+ // CLANG: error: 'send' called with size bigger than buffer
send(0, buf, 6, 0);
}
+
+void test_realpath() {
+ char buf[4] = {0};
+ // NOLINTNEXTLINE(whitespace/line_length)
+ // GCC: error: call to '__realpath_size_error' declared with attribute error: 'realpath' output parameter must be NULL or a pointer to a buffer with >= PATH_MAX bytes
+ // NOLINTNEXTLINE(whitespace/line_length)
+ // CLANG: error: 'realpath' output parameter must be NULL or a pointer to a buffer with >= PATH_MAX bytes
+ realpath(".", buf);
+
+ // This is fine.
+ realpath(".", NULL);
+
+ // FIXME: But we should warn on this.
+ realpath(NULL, buf);
+}
diff --git a/tests/libs/Android.bp b/tests/libs/Android.bp
index 973a8d2..c1600cc 100644
--- a/tests/libs/Android.bp
+++ b/tests/libs/Android.bp
@@ -617,3 +617,59 @@
defaults: ["bionic_testlib_defaults"],
srcs: ["preinit_syscall_test_helper.cpp"],
}
+
+cc_test {
+ name: "ld_preload_test_helper",
+ host_supported: false,
+ defaults: ["bionic_testlib_defaults"],
+ srcs: ["ld_preload_test_helper.cpp"],
+ shared_libs: ["ld_preload_test_helper_lib1"],
+ ldflags: ["-Wl,--rpath,${ORIGIN}/.."],
+}
+
+cc_test_library {
+ name: "ld_preload_test_helper_lib1",
+ host_supported: false,
+ defaults: ["bionic_testlib_defaults"],
+ srcs: ["ld_preload_test_helper_lib1.cpp"],
+}
+
+cc_test_library {
+ name: "ld_preload_test_helper_lib2",
+ host_supported: false,
+ defaults: ["bionic_testlib_defaults"],
+ srcs: ["ld_preload_test_helper_lib2.cpp"],
+}
+
+cc_test {
+ name: "ld_config_test_helper",
+ host_supported: false,
+ defaults: ["bionic_testlib_defaults"],
+ srcs: ["ld_config_test_helper.cpp"],
+ shared_libs: ["ld_config_test_helper_lib1"],
+ ldflags: ["-Wl,--rpath,${ORIGIN}/.."],
+}
+
+cc_test_library {
+ name: "ld_config_test_helper_lib1",
+ host_supported: false,
+ defaults: ["bionic_testlib_defaults"],
+ srcs: ["ld_config_test_helper_lib1.cpp"],
+ shared_libs: ["ld_config_test_helper_lib2"],
+ relative_install_path: "/ns2",
+}
+
+cc_test_library {
+ name: "ld_config_test_helper_lib2",
+ host_supported: false,
+ defaults: ["bionic_testlib_defaults"],
+ srcs: ["ld_config_test_helper_lib2.cpp"],
+ relative_install_path: "/ns2",
+}
+
+cc_test_library {
+ name: "ld_config_test_helper_lib3",
+ host_supported: false,
+ defaults: ["bionic_testlib_defaults"],
+ srcs: ["ld_config_test_helper_lib3.cpp"],
+}
diff --git a/tests/libs/ld_config_test_helper.cpp b/tests/libs/ld_config_test_helper.cpp
new file mode 100644
index 0000000..592e8c0
--- /dev/null
+++ b/tests/libs/ld_config_test_helper.cpp
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2017 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 <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+
+extern int get_value_from_lib();
+
+int main() {
+ printf("%d", get_value_from_lib());
+ return 0;
+}
diff --git a/tests/libs/ld_config_test_helper_lib1.cpp b/tests/libs/ld_config_test_helper_lib1.cpp
new file mode 100644
index 0000000..fc5401a
--- /dev/null
+++ b/tests/libs/ld_config_test_helper_lib1.cpp
@@ -0,0 +1,4 @@
+extern int get_value_from_another_lib();
+int get_value_from_lib() {
+ return get_value_from_another_lib();
+}
diff --git a/tests/libs/ld_config_test_helper_lib2.cpp b/tests/libs/ld_config_test_helper_lib2.cpp
new file mode 100644
index 0000000..a620a6c
--- /dev/null
+++ b/tests/libs/ld_config_test_helper_lib2.cpp
@@ -0,0 +1,3 @@
+int get_value_from_another_lib() {
+ return 12345;
+}
diff --git a/tests/libs/ld_config_test_helper_lib3.cpp b/tests/libs/ld_config_test_helper_lib3.cpp
new file mode 100644
index 0000000..93d1cd8
--- /dev/null
+++ b/tests/libs/ld_config_test_helper_lib3.cpp
@@ -0,0 +1,3 @@
+int get_value_from_another_lib() {
+ return 54321;
+}
diff --git a/tests/libs/ld_preload_test_helper.cpp b/tests/libs/ld_preload_test_helper.cpp
new file mode 100644
index 0000000..592e8c0
--- /dev/null
+++ b/tests/libs/ld_preload_test_helper.cpp
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2017 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 <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+
+extern int get_value_from_lib();
+
+int main() {
+ printf("%d", get_value_from_lib());
+ return 0;
+}
diff --git a/tests/libs/ld_preload_test_helper_lib1.cpp b/tests/libs/ld_preload_test_helper_lib1.cpp
new file mode 100644
index 0000000..74e89db
--- /dev/null
+++ b/tests/libs/ld_preload_test_helper_lib1.cpp
@@ -0,0 +1,3 @@
+int get_value_from_lib() {
+ return 12345;
+}
diff --git a/tests/libs/ld_preload_test_helper_lib2.cpp b/tests/libs/ld_preload_test_helper_lib2.cpp
new file mode 100644
index 0000000..9239891
--- /dev/null
+++ b/tests/libs/ld_preload_test_helper_lib2.cpp
@@ -0,0 +1,3 @@
+int get_value_from_lib() {
+ return 54321;
+}
diff --git a/tests/sched_test.cpp b/tests/sched_test.cpp
index a4cffc0..e70528e 100644
--- a/tests/sched_test.cpp
+++ b/tests/sched_test.cpp
@@ -270,3 +270,34 @@
CPU_FREE(set1);
CPU_FREE(set2);
}
+
+TEST(sched, sched_get_priority_min_sched_get_priority_max) {
+ EXPECT_LE(sched_get_priority_min(SCHED_BATCH), sched_get_priority_max(SCHED_BATCH));
+ EXPECT_LE(sched_get_priority_min(SCHED_FIFO), sched_get_priority_max(SCHED_FIFO));
+ EXPECT_LE(sched_get_priority_min(SCHED_IDLE), sched_get_priority_max(SCHED_IDLE));
+ EXPECT_LE(sched_get_priority_min(SCHED_OTHER), sched_get_priority_max(SCHED_OTHER));
+ EXPECT_LE(sched_get_priority_min(SCHED_RR), sched_get_priority_max(SCHED_RR));
+}
+
+TEST(sched, sched_getscheduler_sched_setscheduler) {
+ // POSIX: "If pid is zero, the scheduling policy shall be returned for the
+ // calling process".
+ ASSERT_EQ(sched_getscheduler(getpid()), sched_getscheduler(0));
+
+ const int original_policy = sched_getscheduler(getpid());
+ sched_param p = {};
+ p.sched_priority = sched_get_priority_min(original_policy);
+ errno = 0;
+ ASSERT_EQ(-1, sched_setscheduler(getpid(), INT_MAX, &p));
+ ASSERT_EQ(EINVAL, errno);
+
+ ASSERT_EQ(0, sched_getparam(getpid(), &p));
+ ASSERT_EQ(original_policy, sched_setscheduler(getpid(), SCHED_BATCH, &p));
+ // POSIX says this should return the previous policy (here SCHED_BATCH),
+ // but the Linux system call doesn't, and the glibc wrapper doesn't correct
+ // this (the "returns 0" behavior is even documented on the man page in
+ // the BUGS section). This was our historical behavior too, so in the
+ // absence of reasons to break compatibility with ourselves and glibc, we
+ // don't behave as POSIX specifies. http://b/26203902.
+ ASSERT_EQ(0, sched_setscheduler(getpid(), original_policy, &p));
+}
diff --git a/tests/stdio_ext_test.cpp b/tests/stdio_ext_test.cpp
index 7872567..128e255 100644
--- a/tests/stdio_ext_test.cpp
+++ b/tests/stdio_ext_test.cpp
@@ -116,20 +116,78 @@
}
TEST(stdio_ext, __freadable__fwritable) {
- FILE* fp = fopen("/dev/null", "r");
+ FILE* fp;
+
+ // Read-only.
+ fp = fopen("/dev/null", "r");
ASSERT_TRUE(__freadable(fp));
ASSERT_FALSE(__fwritable(fp));
fclose(fp);
+ // Write-only.
fp = fopen("/dev/null", "w");
ASSERT_FALSE(__freadable(fp));
ASSERT_TRUE(__fwritable(fp));
fclose(fp);
- fp = fopen("/dev/null", "w+");
- ASSERT_TRUE(__freadable(fp));
+ // Append (aka write-only).
+ fp = fopen("/dev/null", "a");
+ ASSERT_FALSE(__freadable(fp));
ASSERT_TRUE(__fwritable(fp));
fclose(fp);
+
+ // The three read-write modes.
+ for (auto read_write_mode : {"r+", "w+", "a+"}) {
+ fp = fopen("/dev/null", read_write_mode);
+ ASSERT_TRUE(__freadable(fp));
+ ASSERT_TRUE(__fwritable(fp));
+ fclose(fp);
+ }
+}
+
+TEST(stdio_ext, __freading__fwriting) {
+ FILE* fp;
+
+ // Append (aka write-only). Never reading. Always writing.
+ fp = fopen("/dev/zero", "a");
+ ASSERT_FALSE(__freading(fp)); // Not reading initially.
+ ASSERT_TRUE(__fwriting(fp)); // Writing initially.
+ ASSERT_TRUE(fputc('x', fp) != EOF);
+ ASSERT_FALSE(__freading(fp)); // Not reading after write.
+ ASSERT_TRUE(__fwriting(fp)); // Still writing after write.
+ fclose(fp);
+
+ // Write-only. Never reading. Always writing.
+ fp = fopen("/dev/zero", "w");
+ ASSERT_FALSE(__freading(fp)); // Not reading initially.
+ ASSERT_TRUE(__fwriting(fp)); // Writing initially.
+ ASSERT_TRUE(fputc('x', fp) != EOF);
+ ASSERT_FALSE(__freading(fp)); // Not reading after write.
+ ASSERT_TRUE(__fwriting(fp)); // Still writing after write.
+ fclose(fp);
+
+ // Read-only. Always reading. Never writing.
+ fp = fopen("/dev/zero", "r");
+ ASSERT_TRUE(__freading(fp)); // Reading initially.
+ ASSERT_FALSE(__fwriting(fp)); // Not writing initially.
+ ASSERT_TRUE(fgetc(fp) == 0);
+ ASSERT_TRUE(__freading(fp)); // Still reading after read.
+ ASSERT_FALSE(__fwriting(fp)); // Still not writing after read.
+ fclose(fp);
+
+ // The three read-write modes.
+ for (auto read_write_mode : {"r+", "w+", "a+"}) {
+ fp = fopen("/dev/zero", read_write_mode);
+ ASSERT_FALSE(__freading(fp)); // Not reading initially.
+ ASSERT_FALSE(__fwriting(fp)); // Not writing initially.
+ ASSERT_TRUE(fgetc(fp) == 0);
+ ASSERT_TRUE(__freading(fp)); // Reading after read.
+ ASSERT_FALSE(__fwriting(fp)); // Not writing after read.
+ ASSERT_TRUE(fputc('x', fp) != EOF);
+ ASSERT_FALSE(__freading(fp)); // Not reading after write.
+ ASSERT_TRUE(__fwriting(fp)); // Writing after write.
+ fclose(fp);
+ }
}
TEST(stdio_ext, __fsetlocking) {