Merge "Document a couple of new-in-U features."
diff --git a/apex/Android.bp b/apex/Android.bp
index 90a14b2..6201ad1 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -41,6 +41,11 @@
"libc_malloc_debug",
"libc_malloc_hooks",
],
+ arch: {
+ arm64: {
+ native_shared_libs: ["libc_hwasan", "libclang_rt.hwasan"],
+ },
+ },
binaries: [
"linkerconfig",
],
diff --git a/libc/Android.bp b/libc/Android.bp
index 7eaa1bf..f5f5f7c 100644
--- a/libc/Android.bp
+++ b/libc/Android.bp
@@ -1674,13 +1674,12 @@
// ========================================================
// libc.a + libc.so
// ========================================================
-cc_library {
+cc_defaults {
defaults: [
"libc_defaults",
"libc_native_allocator_defaults",
],
- name: "libc",
- static_ndk_lib: true,
+ name: "libc_library_defaults",
product_variables: {
platform_sdk_version: {
asflags: ["-DPLATFORM_SDK_VERSION=%d"],
@@ -1807,20 +1806,7 @@
},
},
- stubs: {
- symbol_file: "libc.map.txt",
- versions: [
- "29",
- "R",
- "current",
- ],
- },
- llndk: {
- symbol_file: "libc.map.txt",
- export_headers_as_system: true,
- export_preprocessed_headers: ["include"],
- export_llndk_headers: ["libc_llndk_headers"],
- },
+
apex_available: [
"//apex_available:platform",
"com.android.runtime",
@@ -1835,6 +1821,55 @@
},
}
+cc_library {
+ name: "libc",
+ defaults: [
+ "libc_library_defaults",
+ ],
+ stubs: {
+ symbol_file: "libc.map.txt",
+ versions: [
+ "29",
+ "R",
+ "current",
+ ],
+ },
+ static_ndk_lib: true,
+ llndk: {
+ symbol_file: "libc.map.txt",
+ export_headers_as_system: true,
+ export_preprocessed_headers: ["include"],
+ export_llndk_headers: ["libc_llndk_headers"],
+ },
+}
+
+cc_library {
+ name: "libc_hwasan",
+ defaults: [
+ "libc_library_defaults",
+ ],
+ sanitize: {
+ hwaddress: true,
+ },
+ enabled: false,
+ target: {
+ android_arm64: {
+ enabled: true,
+ },
+ },
+ stem: "libc",
+ relative_install_path: "hwasan",
+ // We don't really need the stubs, but this needs to stay to trigger the
+ // symlink logic in soong.
+ stubs: {
+ symbol_file: "libc.map.txt",
+ },
+ native_bridge_supported: false,
+ // It is never correct to depend on this directly. This is only
+ // needed for the runtime apex, and in base_system.mk.
+ visibility: ["//bionic/apex"],
+}
+
genrule {
name: "libc.arm.map",
out: ["libc.arm.map.txt"],
diff --git a/libc/bionic/libc_init_common.cpp b/libc/bionic/libc_init_common.cpp
index 5d5ecac..59b2ddb 100644
--- a/libc/bionic/libc_init_common.cpp
+++ b/libc/bionic/libc_init_common.cpp
@@ -289,6 +289,7 @@
"LD_DEBUG",
"LD_DEBUG_OUTPUT",
"LD_DYNAMIC_WEAK",
+ "LD_HWASAN",
"LD_LIBRARY_PATH",
"LD_ORIGIN_PATH",
"LD_PRELOAD",
diff --git a/libc/bionic/pthread_internal.h b/libc/bionic/pthread_internal.h
index a3a4ccd..106f3ed 100644
--- a/libc/bionic/pthread_internal.h
+++ b/libc/bionic/pthread_internal.h
@@ -137,7 +137,7 @@
// clobber x18 on arm64, therefore each process must declare early during
// process startup whether it might load legacy code.
// TODO: riscv64 has no legacy code, so we can actually go this route
- // there, but hopefully we'll actually get the Zsslpcfi extension instead.
+ // there, but hopefully we'll actually get the Zisslpcfi extension instead.
// 2) Mark the guard region as such using prctl(PR_SET_VMA_ANON_NAME) and
// discover its address by reading /proc/self/maps. One issue with this is
// that reading /proc/self/maps can race with allocations, so we may need
diff --git a/libc/bionic/tmpfile.cpp b/libc/bionic/tmpfile.cpp
index 3d04610..4d6a1fb 100644
--- a/libc/bionic/tmpfile.cpp
+++ b/libc/bionic/tmpfile.cpp
@@ -51,6 +51,9 @@
return nullptr;
}
+// O_TMPFILE isn't available until Linux 3.11, so we fall back to this on
+// older kernels. AOSP was on a new enough kernel in the Lollipop timeframe,
+// so this code should be obsolete by 2025.
static FILE* __tmpfile_dir_legacy(const char* tmp_dir) {
char* path = nullptr;
if (asprintf(&path, "%s/tmp.XXXXXXXXXX", tmp_dir) == -1) {
@@ -79,25 +82,18 @@
return __fd_to_fp(fd);
}
-static FILE* __tmpfile_dir(const char* tmp_dir) {
- int fd = open(tmp_dir, O_TMPFILE | O_RDWR, S_IRUSR | S_IWUSR);
- if (fd == -1) return __tmpfile_dir_legacy(tmp_dir);
- return __fd_to_fp(fd);
+const char* __get_TMPDIR() {
+ // Use $TMPDIR if set, or fall back to /data/local/tmp otherwise.
+ // Useless for apps, but good enough for the shell.
+ const char* tmpdir = getenv("TMPDIR");
+ return (tmpdir == nullptr) ? "/data/local/tmp" : tmpdir;
}
FILE* tmpfile() {
- // TODO: get this app's temporary directory from the framework ("/data/data/app/cache").
-
- // $EXTERNAL_STORAGE turns out not to be very useful because it doesn't support hard links.
- // This means we can't do the usual trick of calling unlink before handing the file back.
-
- FILE* fp = __tmpfile_dir("/data/local/tmp");
- if (fp == nullptr) {
- // P_tmpdir is "/tmp/", but POSIX explicitly says that tmpdir(3) should try P_tmpdir before
- // giving up. This is potentially useful for bionic on the host anyway.
- fp = __tmpfile_dir(P_tmpdir);
- }
- return fp;
+ const char* tmpdir = __get_TMPDIR();
+ int fd = open(tmpdir, O_TMPFILE | O_RDWR, S_IRUSR | S_IWUSR);
+ if (fd == -1) return __tmpfile_dir_legacy(tmpdir);
+ return __fd_to_fp(fd);
}
__strong_alias(tmpfile64, tmpfile);
@@ -107,7 +103,7 @@
// since we can't easily remove it...
// $TMPDIR overrides any directory passed in.
- char* tmpdir = getenv("TMPDIR");
+ const char* tmpdir = getenv("TMPDIR");
if (tmpdir != nullptr) dir = tmpdir;
// If we still have no directory, we'll give you a default.
@@ -136,12 +132,7 @@
static char buf[L_tmpnam];
if (s == nullptr) s = buf;
- // Use $TMPDIR if set, or fall back to /data/local/tmp otherwise.
- // Useless for apps, but good enough for the shell.
- const char* dir = getenv("TMPDIR");
- if (dir == nullptr) dir = "/data/local/tmp";
-
// Make up a mktemp(3) template and defer to it for the real work.
- snprintf(s, L_tmpnam, "%s/tmpnam.XXXXXXXXXX", dir);
+ snprintf(s, L_tmpnam, "%s/tmpnam.XXXXXXXXXX", __get_TMPDIR());
return mktemp(s);
}
diff --git a/libc/include/sys/capability.h b/libc/include/sys/capability.h
index 4cb698f..b43bbf0 100644
--- a/libc/include/sys/capability.h
+++ b/libc/include/sys/capability.h
@@ -44,7 +44,7 @@
*
* Returns 0 on success, and returns -1 and sets `errno` on failure.
*/
-int capget(cap_user_header_t __hdr_ptr, cap_user_data_t __data_ptr);
+int capget(cap_user_header_t _Nonnull __hdr_ptr, cap_user_data_t _Nullable __data_ptr);
/**
* [capset(2)](http://man7.org/linux/man-pages/man2/capset.2.html) sets the calling
@@ -52,6 +52,6 @@
*
* Returns 0 on success, and returns -1 and sets `errno` on failure.
*/
-int capset(cap_user_header_t __hdr_ptr, const cap_user_data_t __data_ptr);
+int capset(cap_user_header_t _Nonnull __hdr_ptr, const cap_user_data_t _Nullable __data_ptr);
__END_DECLS
diff --git a/libc/include/sys/inotify.h b/libc/include/sys/inotify.h
index c3cdc85..e834d07 100644
--- a/libc/include/sys/inotify.h
+++ b/libc/include/sys/inotify.h
@@ -42,7 +42,7 @@
int inotify_init(void);
int inotify_init1(int __flags) __INTRODUCED_IN(21);
-int inotify_add_watch(int __fd, const char* __path, uint32_t __mask);
+int inotify_add_watch(int __fd, const char* _Nonnull __path, uint32_t __mask);
int inotify_rm_watch(int __fd, uint32_t __watch_descriptor);
__END_DECLS
diff --git a/libc/include/sys/ipc.h b/libc/include/sys/ipc.h
index c81ec1a..2e2b8cf 100644
--- a/libc/include/sys/ipc.h
+++ b/libc/include/sys/ipc.h
@@ -52,6 +52,6 @@
*
* Returns a key on success, and returns -1 and sets `errno` on failure.
*/
-key_t ftok(const char* __path, int __id);
+key_t ftok(const char* _Nonnull __path, int __id);
__END_DECLS
diff --git a/libc/include/sys/mount.h b/libc/include/sys/mount.h
index 4db1ac1..aace205 100644
--- a/libc/include/sys/mount.h
+++ b/libc/include/sys/mount.h
@@ -55,7 +55,7 @@
*
* Returns 0 on success, and returns -1 and sets `errno` on failure.
*/
-int mount(const char* __source, const char* __target, const char* __fs_type, unsigned long __flags, const void* __data);
+int mount(const char* __BIONIC_COMPLICATED_NULLNESS __source, const char* _Nonnull __target, const char* __BIONIC_COMPLICATED_NULLNESS __fs_type, unsigned long __flags, const void* _Nullable __data);
/**
* [umount(2)](http://man7.org/linux/man-pages/man2/umount.2.html) unmounts the filesystem at
@@ -63,7 +63,7 @@
*
* Returns 0 on success, and returns -1 and sets `errno` on failure.
*/
-int umount(const char* __target);
+int umount(const char* _Nonnull __target);
/**
* [umount2(2)](http://man7.org/linux/man-pages/man2/umount2.2.html) unmounts the filesystem at
@@ -71,6 +71,6 @@
*
* Returns 0 on success, and returns -1 and sets `errno` on failure.
*/
-int umount2(const char* __target, int __flags);
+int umount2(const char* _Nonnull __target, int __flags);
__END_DECLS
diff --git a/libc/include/sys/msg.h b/libc/include/sys/msg.h
index e19452c..ad481a0 100644
--- a/libc/include/sys/msg.h
+++ b/libc/include/sys/msg.h
@@ -46,12 +46,12 @@
typedef __kernel_ulong_t msglen_t;
/** Not useful on Android; disallowed by SELinux. */
-int msgctl(int __msg_id, int __cmd, struct msqid_ds* __buf) __INTRODUCED_IN(26);
+int msgctl(int __msg_id, int __cmd, struct msqid_ds* _Nullable __buf) __INTRODUCED_IN(26);
/** Not useful on Android; disallowed by SELinux. */
int msgget(key_t __key, int __flags) __INTRODUCED_IN(26);
/** Not useful on Android; disallowed by SELinux. */
-ssize_t msgrcv(int __msg_id, void* __msgbuf_ptr, size_t __size, long __type, int __flags) __INTRODUCED_IN(26);
+ssize_t msgrcv(int __msg_id, void* _Nonnull __msgbuf_ptr, size_t __size, long __type, int __flags) __INTRODUCED_IN(26);
/** Not useful on Android; disallowed by SELinux. */
-int msgsnd(int __msg_id, const void* __msgbuf_ptr, size_t __size, int __flags) __INTRODUCED_IN(26);
+int msgsnd(int __msg_id, const void* _Nonnull __msgbuf_ptr, size_t __size, int __flags) __INTRODUCED_IN(26);
__END_DECLS
diff --git a/libc/include/sys/random.h b/libc/include/sys/random.h
index be52bd9..0251176 100644
--- a/libc/include/sys/random.h
+++ b/libc/include/sys/random.h
@@ -50,7 +50,7 @@
*
* See also arc4random_buf() which is available in all API levels.
*/
-int getentropy(void* __buffer, size_t __buffer_size) __wur __INTRODUCED_IN(28);
+int getentropy(void* _Nonnull __buffer, size_t __buffer_size) __wur __INTRODUCED_IN(28);
/**
* [getrandom(2)](http://man7.org/linux/man-pages/man2/getrandom.2.html) fills the given buffer
@@ -62,6 +62,6 @@
*
* See also arc4random_buf() which is available in all API levels.
*/
-ssize_t getrandom(void* __buffer, size_t __buffer_size, unsigned int __flags) __wur __INTRODUCED_IN(28);
+ssize_t getrandom(void* _Nonnull __buffer, size_t __buffer_size, unsigned int __flags) __wur __INTRODUCED_IN(28);
__END_DECLS
diff --git a/libc/include/sys/select.h b/libc/include/sys/select.h
index 06914a6..8c6c2ff 100644
--- a/libc/include/sys/select.h
+++ b/libc/include/sys/select.h
@@ -71,9 +71,9 @@
} \
} while (0)
-void __FD_CLR_chk(int, fd_set*, size_t) __INTRODUCED_IN(21);
-void __FD_SET_chk(int, fd_set*, size_t) __INTRODUCED_IN(21);
-int __FD_ISSET_chk(int, const fd_set*, size_t) __INTRODUCED_IN(21);
+void __FD_CLR_chk(int, fd_set* _Nonnull , size_t) __INTRODUCED_IN(21);
+void __FD_SET_chk(int, fd_set* _Nonnull, size_t) __INTRODUCED_IN(21);
+int __FD_ISSET_chk(int, const fd_set* _Nonnull, size_t) __INTRODUCED_IN(21);
#define __FD_CLR(fd, set) (__FDS_BITS(fd_set*,set)[__FDELT(fd)] &= ~__FDMASK(fd))
#define __FD_SET(fd, set) (__FDS_BITS(fd_set*,set)[__FDELT(fd)] |= __FDMASK(fd))
@@ -95,7 +95,7 @@
* Returns the number of ready file descriptors on success, 0 for timeout,
* and returns -1 and sets `errno` on failure.
*/
-int select(int __max_fd_plus_one, fd_set* __read_fds, fd_set* __write_fds, fd_set* __exception_fds, struct timeval* __timeout);
+int select(int __max_fd_plus_one, fd_set* _Nullable __read_fds, fd_set* _Nullable __write_fds, fd_set* _Nullable __exception_fds, struct timeval* _Nullable __timeout);
/**
* [pselect(2)](http://man7.org/linux/man-pages/man2/select.2.html) waits on a
@@ -106,7 +106,7 @@
* Returns the number of ready file descriptors on success, 0 for timeout,
* and returns -1 and sets `errno` on failure.
*/
-int pselect(int __max_fd_plus_one, fd_set* __read_fds, fd_set* __write_fds, fd_set* __exception_fds, const struct timespec* __timeout, const sigset_t* __mask);
+int pselect(int __max_fd_plus_one, fd_set* _Nullable __read_fds, fd_set* _Nullable __write_fds, fd_set* _Nullable __exception_fds, const struct timespec* _Nullable __timeout, const sigset_t* _Nullable __mask);
/**
* [pselect64(2)](http://man7.org/linux/man-pages/man2/select.2.html) waits on a
@@ -119,6 +119,6 @@
*
* Available since API level 28.
*/
-int pselect64(int __max_fd_plus_one, fd_set* __read_fds, fd_set* __write_fds, fd_set* __exception_fds, const struct timespec* __timeout, const sigset64_t* __mask) __INTRODUCED_IN(28);
+int pselect64(int __max_fd_plus_one, fd_set* _Nullable __read_fds, fd_set* _Nullable __write_fds, fd_set* _Nullable __exception_fds, const struct timespec* _Nullable __timeout, const sigset64_t* _Nullable __mask) __INTRODUCED_IN(28);
__END_DECLS
diff --git a/libc/include/sys/sem.h b/libc/include/sys/sem.h
index cd62242..f4256e2 100644
--- a/libc/include/sys/sem.h
+++ b/libc/include/sys/sem.h
@@ -45,18 +45,18 @@
union semun {
int val;
- struct semid_ds* buf;
- unsigned short* array;
- struct seminfo* __buf;
- void* __pad;
+ struct semid_ds* _Nullable buf;
+ unsigned short* _Nullable array;
+ struct seminfo* _Nullable __buf;
+ void* _Nullable __pad;
};
int semctl(int __sem_id, int __sem_num, int __cmd, ...) __INTRODUCED_IN(26);
int semget(key_t __key, int __sem_count, int __flags) __INTRODUCED_IN(26);
-int semop(int __sem_id, struct sembuf* __ops, size_t __op_count) __INTRODUCED_IN(26);
+int semop(int __sem_id, struct sembuf* _Nonnull __ops, size_t __op_count) __INTRODUCED_IN(26);
#if defined(__USE_GNU)
-int semtimedop(int __sem_id, struct sembuf* __ops, size_t __op_count, const struct timespec* __timeout) __INTRODUCED_IN(26);
+int semtimedop(int __sem_id, struct sembuf* _Nonnull __ops, size_t __op_count, const struct timespec* _Nullable __timeout) __INTRODUCED_IN(26);
#endif
__END_DECLS
diff --git a/libc/include/sys/sendfile.h b/libc/include/sys/sendfile.h
index 60bbde8..4b00d5d 100644
--- a/libc/include/sys/sendfile.h
+++ b/libc/include/sys/sendfile.h
@@ -40,7 +40,7 @@
/* See https://android.googlesource.com/platform/bionic/+/master/docs/32-bit-abi.md */
#if defined(__USE_FILE_OFFSET64)
-ssize_t sendfile(int __out_fd, int __in_fd, off_t* __offset, size_t __count) __RENAME(sendfile64) __INTRODUCED_IN(21);
+ssize_t sendfile(int __out_fd, int __in_fd, off_t* _Nullable __offset, size_t __count) __RENAME(sendfile64) __INTRODUCED_IN(21);
#else
/**
* [sendfile(2)](http://man7.org/linux/man-pages/man2/sendfile.2.html) copies data directly
@@ -50,13 +50,13 @@
*
* Available since API level 21.
*/
-ssize_t sendfile(int __out_fd, int __in_fd, off_t* __offset, size_t __count);
+ssize_t sendfile(int __out_fd, int __in_fd, off_t* _Nullable __offset, size_t __count);
#endif
/**
* Like sendfile() but allows using a 64-bit offset
* even from a 32-bit process without `__FILE_OFFSET_BITS=64`.
*/
-ssize_t sendfile64(int __out_fd, int __in_fd, off64_t* __offset, size_t __count) __INTRODUCED_IN(21);
+ssize_t sendfile64(int __out_fd, int __in_fd, off64_t* _Nullable __offset, size_t __count) __INTRODUCED_IN(21);
__END_DECLS
diff --git a/libc/include/sys/statvfs.h b/libc/include/sys/statvfs.h
index 793ee13..d81f836 100644
--- a/libc/include/sys/statvfs.h
+++ b/libc/include/sys/statvfs.h
@@ -96,7 +96,7 @@
*
* Available since API level 19.
*/
-int statvfs(const char* __path, struct statvfs* __buf) __INTRODUCED_IN(19);
+int statvfs(const char* _Nonnull __path, struct statvfs* _Nonnull __buf) __INTRODUCED_IN(19);
/**
* [fstatvfs(3)](http://man7.org/linux/man-pages/man3/fstatvfs.3.html)
@@ -106,12 +106,12 @@
*
* Available since API level 19.
*/
-int fstatvfs(int __fd, struct statvfs* __buf) __INTRODUCED_IN(19);
+int fstatvfs(int __fd, struct statvfs* _Nonnull __buf) __INTRODUCED_IN(19);
/** Equivalent to statvfs() . */
-int statvfs64(const char* __path, struct statvfs64* __buf) __INTRODUCED_IN(21);
+int statvfs64(const char* _Nonnull __path, struct statvfs64* _Nonnull __buf) __INTRODUCED_IN(21);
/** Equivalent to fstatvfs(). */
-int fstatvfs64(int __fd, struct statvfs64* __buf) __INTRODUCED_IN(21);
+int fstatvfs64(int __fd, struct statvfs64* _Nonnull __buf) __INTRODUCED_IN(21);
__END_DECLS
diff --git a/libc/include/sys/wait.h b/libc/include/sys/wait.h
index 96974a2..e6fb855 100644
--- a/libc/include/sys/wait.h
+++ b/libc/include/sys/wait.h
@@ -37,9 +37,9 @@
__BEGIN_DECLS
-pid_t wait(int* __status);
-pid_t waitpid(pid_t __pid, int* __status, int __options);
-pid_t wait4(pid_t __pid, int* __status, int __options, struct rusage* __rusage) __INTRODUCED_IN(18);
+pid_t wait(int* _Nullable __status);
+pid_t waitpid(pid_t __pid, int* _Nullable __status, int __options);
+pid_t wait4(pid_t __pid, int* _Nullable __status, int __options, struct rusage* _Nullable __rusage) __INTRODUCED_IN(18);
/* Posix states that idtype_t should be an enumeration type, but
* the kernel headers define P_ALL, P_PID and P_PGID as constant macros
@@ -47,6 +47,6 @@
*/
typedef int idtype_t;
-int waitid(idtype_t __type, id_t __id, siginfo_t* __info, int __options);
+int waitid(idtype_t __type, id_t __id, siginfo_t* _Nullable __info, int __options);
__END_DECLS
diff --git a/libc/include/wchar.h b/libc/include/wchar.h
index add3606..39f9374 100644
--- a/libc/include/wchar.h
+++ b/libc/include/wchar.h
@@ -44,97 +44,96 @@
__BEGIN_DECLS
wint_t btowc(int __ch);
-int fwprintf(FILE* __fp, const wchar_t* __fmt, ...);
-int fwscanf(FILE* __fp, const wchar_t* __fmt, ...);
-wint_t fgetwc(FILE* __fp);
-wchar_t* fgetws(wchar_t* __buf, int __size, FILE* __fp);
-wint_t fputwc(wchar_t __wc, FILE* __fp);
-int fputws(const wchar_t* __s, FILE* __fp);
-int fwide(FILE* __fp, int __mode);
-wint_t getwc(FILE* __fp);
+int fwprintf(FILE* _Nonnull __fp, const wchar_t* _Nonnull __fmt, ...);
+int fwscanf(FILE* _Nonnull __fp, const wchar_t* _Nonnull __fmt, ...);
+wint_t fgetwc(FILE* _Nonnull __fp);
+wchar_t* _Nullable fgetws(wchar_t* _Nonnull __buf, int __size, FILE* _Nonnull __fp);
+wint_t fputwc(wchar_t __wc, FILE* _Nonnull __fp);
+int fputws(const wchar_t* _Nonnull __s, FILE* _Nonnull __fp);
+int fwide(FILE* _Nonnull __fp, int __mode);
+wint_t getwc(FILE* _Nonnull __fp);
wint_t getwchar(void);
-int mbsinit(const mbstate_t* __ps);
-size_t mbrlen(const char* __s, size_t __n, mbstate_t* __ps);
-size_t mbrtowc(wchar_t* __buf, const char* __s, size_t __n, mbstate_t* __ps);
-size_t mbsrtowcs(wchar_t* __dst, const char** __src, size_t __dst_n, mbstate_t* __ps);
-size_t mbsnrtowcs(wchar_t* __dst, const char** __src, size_t __src_n, size_t __dst_n, mbstate_t* __ps) __INTRODUCED_IN(21);
-wint_t putwc(wchar_t __wc, FILE* __fp);
+int mbsinit(const mbstate_t* _Nullable __ps);
+size_t mbrlen(const char* _Nullable __s, size_t __n, mbstate_t* _Nullable __ps);
+size_t mbrtowc(wchar_t* _Nullable __buf, const char* _Nullable __s, size_t __n, mbstate_t* _Nullable __ps);
+size_t mbsrtowcs(wchar_t* _Nullable __dst, const char* _Nullable * _Nonnull __src, size_t __dst_n, mbstate_t* _Nullable __ps);
+size_t mbsnrtowcs(wchar_t* _Nullable __dst, const char* _Nullable * _Nullable __src, size_t __src_n, size_t __dst_n, mbstate_t* _Nullable __ps) __INTRODUCED_IN(21);
+wint_t putwc(wchar_t __wc, FILE* _Nonnull __fp);
wint_t putwchar(wchar_t __wc);
-int swprintf(wchar_t* __buf, size_t __n, const wchar_t* __fmt, ...);
-int swscanf(const wchar_t* __s, const wchar_t* __fmt, ...);
-wint_t ungetwc(wint_t __wc, FILE* __fp);
-int vfwprintf(FILE* __fp, const wchar_t* __fmt, va_list __args);
-int vfwscanf(FILE* __fp, const wchar_t* __fmt, va_list __args) __INTRODUCED_IN(21);
-int vswprintf(wchar_t* __buf, size_t __n, const wchar_t* __fmt, va_list __args);
-int vswscanf(const wchar_t* __s, const wchar_t* __fmt, va_list __args) __INTRODUCED_IN(21);
-int vwprintf(const wchar_t* __fmt, va_list __args);
-int vwscanf(const wchar_t* __fmt, va_list __args) __INTRODUCED_IN(21);
-wchar_t* wcpcpy(wchar_t* __dst, const wchar_t* __src);
-wchar_t* wcpncpy(wchar_t* __dst, const wchar_t* __src, size_t __n);
-size_t wcrtomb(char* __buf, wchar_t __wc, mbstate_t* __ps);
-int wcscasecmp(const wchar_t* __lhs, const wchar_t* __rhs);
-int wcscasecmp_l(const wchar_t* __lhs, const wchar_t* __rhs, locale_t __l) __INTRODUCED_IN(23);
-wchar_t* wcscat(wchar_t* __dst, const wchar_t* __src);
-wchar_t* wcschr(const wchar_t* __s, wchar_t __wc);
-int wcscmp(const wchar_t* __lhs, const wchar_t* __rhs);
-int wcscoll(const wchar_t* __lhs, const wchar_t* __rhs);
-wchar_t* wcscpy(wchar_t* __dst, const wchar_t* __src);
-size_t wcscspn(const wchar_t* __s, const wchar_t* __accept);
-size_t wcsftime(wchar_t* __buf, size_t __n, const wchar_t* __fmt, const struct tm* __tm);
-size_t wcsftime_l(wchar_t* __buf, size_t __n, const wchar_t* __fmt, const struct tm* __tm, locale_t __l) __INTRODUCED_IN(28);
-size_t wcslen(const wchar_t* __s);
-int wcsncasecmp(const wchar_t* __lhs, const wchar_t* __rhs, size_t __n);
-int wcsncasecmp_l(const wchar_t* __lhs, const wchar_t* __rhs, size_t __n, locale_t __l) __INTRODUCED_IN(23);
-wchar_t* wcsncat(wchar_t* __dst, const wchar_t* __src, size_t __n);
-int wcsncmp(const wchar_t* __lhs, const wchar_t* __rhs, size_t __n);
-wchar_t* wcsncpy(wchar_t* __dst, const wchar_t* __src, size_t __n);
-size_t wcsnrtombs(char* __dst, const wchar_t** __src, size_t __src_n, size_t __dst_n, mbstate_t* __ps) __INTRODUCED_IN(21);
-wchar_t* wcspbrk(const wchar_t* __s, const wchar_t* __accept);
-wchar_t* wcsrchr(const wchar_t* __s, wchar_t __wc);
-size_t wcsrtombs(char* __dst, const wchar_t** __src, size_t __dst_n, mbstate_t* __ps);
-size_t wcsspn(const wchar_t* __s, const wchar_t* __accept);
-wchar_t* wcsstr(const wchar_t* __haystack, const wchar_t* __needle);
-double wcstod(const wchar_t* __s, wchar_t** __end_ptr);
-double wcstod_l(const wchar_t* __s, wchar_t** __end_ptr, locale_t __l) __INTRODUCED_IN(28);
-float wcstof(const wchar_t* __s, wchar_t** __end_ptr) __INTRODUCED_IN(21);
-float wcstof_l(const wchar_t* __s, wchar_t** __end_ptr, locale_t __l) __INTRODUCED_IN(28);
-wchar_t* wcstok(wchar_t* __s, const wchar_t* __delimiter, wchar_t** __ptr);
-long wcstol(const wchar_t* __s, wchar_t** __end_ptr, int __base);
-long wcstol_l(const wchar_t* __s, wchar_t** __end_ptr, int __base, locale_t __l) __INTRODUCED_IN(28);
-long long wcstoll(const wchar_t* __s, wchar_t** __end_ptr, int __base) __INTRODUCED_IN(21);
-long double wcstold(const wchar_t* __s, wchar_t** __end_ptr) __RENAME_LDBL(wcstod, 3, 21);
-unsigned long wcstoul(const wchar_t* __s, wchar_t** __end_ptr, int __base);
-unsigned long wcstoul_l(const wchar_t* __s, wchar_t** __end_ptr, int __base, locale_t __l) __INTRODUCED_IN(28);
-unsigned long long wcstoull(const wchar_t* __s, wchar_t** __end_ptr, int __base) __INTRODUCED_IN(21);
-int wcswidth(const wchar_t* __s, size_t __n);
-size_t wcsxfrm(wchar_t* __dst, const wchar_t* __src, size_t __n);
+int swprintf(wchar_t* _Nonnull __buf, size_t __n, const wchar_t* _Nonnull __fmt, ...);
+int swscanf(const wchar_t* _Nonnull __s, const wchar_t* _Nonnull __fmt, ...);
+wint_t ungetwc(wint_t __wc, FILE* _Nonnull __fp);
+int vfwprintf(FILE* _Nonnull __fp, const wchar_t* _Nonnull __fmt, va_list __args);
+int vfwscanf(FILE* _Nonnull __fp, const wchar_t* _Nonnull __fmt, va_list __args) __INTRODUCED_IN(21);
+int vswprintf(wchar_t* _Nonnull __buf, size_t __n, const wchar_t* _Nonnull __fmt, va_list __args);
+int vswscanf(const wchar_t* _Nonnull __s, const wchar_t* _Nonnull __fmt, va_list __args) __INTRODUCED_IN(21);
+int vwprintf(const wchar_t* _Nonnull __fmt, va_list __args);
+int vwscanf(const wchar_t* _Nonnull __fmt, va_list __args) __INTRODUCED_IN(21);
+wchar_t* _Nonnull wcpcpy(wchar_t* _Nonnull __dst, const wchar_t* _Nonnull __src);
+wchar_t* _Nonnull wcpncpy(wchar_t* _Nonnull __dst, const wchar_t* _Nonnull __src, size_t __n);
+size_t wcrtomb(char* _Nullable __buf, wchar_t __wc, mbstate_t* _Nullable __ps);
+int wcscasecmp(const wchar_t* _Nonnull __lhs, const wchar_t* _Nonnull __rhs);
+int wcscasecmp_l(const wchar_t* _Nonnull __lhs, const wchar_t* _Nonnull __rhs, locale_t _Nonnull __l) __INTRODUCED_IN(23);
+wchar_t* _Nonnull wcscat(wchar_t* _Nonnull __dst, const wchar_t* _Nonnull __src);
+wchar_t* _Nullable wcschr(const wchar_t * _Nonnull __s, wchar_t __wc);
+int wcscmp(const wchar_t* _Nonnull __lhs, const wchar_t* _Nonnull __rhs);
+int wcscoll(const wchar_t* _Nonnull __lhs, const wchar_t* _Nonnull __rhs);
+wchar_t* _Nonnull wcscpy(wchar_t* _Nonnull __dst, const wchar_t* _Nonnull __src);
+size_t wcscspn(const wchar_t* _Nonnull __s, const wchar_t* _Nonnull __accept);
+size_t wcsftime(wchar_t* _Nonnull __buf, size_t __n, const wchar_t* _Nullable __fmt, const struct tm* _Nonnull __tm);
+size_t wcsftime_l(wchar_t* _Nonnull __buf, size_t __n, const wchar_t* _Nullable __fmt, const struct tm* _Nonnull __tm, locale_t _Nonnull __l) __INTRODUCED_IN(28);
+size_t wcslen(const wchar_t* _Nonnull __s);
+int wcsncasecmp(const wchar_t* _Nonnull __lhs, const wchar_t* _Nonnull __rhs, size_t __n);
+int wcsncasecmp_l(const wchar_t* _Nonnull __lhs, const wchar_t* _Nonnull __rhs, size_t __n, locale_t _Nonnull __l) __INTRODUCED_IN(23);
+wchar_t* _Nonnull wcsncat(wchar_t* _Nonnull __dst, const wchar_t* _Nonnull __src, size_t __n);
+int wcsncmp(const wchar_t* _Nonnull __lhs, const wchar_t* _Nonnull __rhs, size_t __n);
+wchar_t* _Nonnull wcsncpy(wchar_t* _Nonnull __dst, const wchar_t* _Nonnull __src, size_t __n);
+size_t wcsnrtombs(char* _Nullable __dst, const wchar_t* __BIONIC_COMPLICATED_NULLNESS * _Nullable __src, size_t __src_n, size_t __dst_n, mbstate_t* _Nullable __ps) __INTRODUCED_IN(21);
+wchar_t* _Nullable wcspbrk(const wchar_t* _Nonnull __s, const wchar_t* _Nonnull __accept);
+wchar_t* _Nullable wcsrchr(const wchar_t* _Nonnull __s, wchar_t __wc);
+size_t wcsrtombs(char* _Nullable __dst, const wchar_t* __BIONIC_COMPLICATED_NULLNESS * _Nullable __src, size_t __dst_n, mbstate_t* _Nullable __ps);
+size_t wcsspn(const wchar_t* _Nonnull __s, const wchar_t* _Nonnull __accept);
+wchar_t* _Nullable wcsstr(const wchar_t* _Nonnull __haystack, const wchar_t* _Nonnull __needle);
+double wcstod(const wchar_t* _Nonnull __s, wchar_t* __BIONIC_COMPLICATED_NULLNESS * _Nullable __end_ptr);
+double wcstod_l(const wchar_t* _Nonnull __s, wchar_t* __BIONIC_COMPLICATED_NULLNESS * _Nullable __end_ptr, locale_t _Nonnull __l) __INTRODUCED_IN(28);
+float wcstof(const wchar_t* _Nonnull __s, wchar_t* __BIONIC_COMPLICATED_NULLNESS * _Nullable __end_ptr) __INTRODUCED_IN(21);
+float wcstof_l(const wchar_t* _Nonnull __s, wchar_t* __BIONIC_COMPLICATED_NULLNESS * _Nullable __end_ptr, locale_t _Nonnull __l) __INTRODUCED_IN(28);
+wchar_t* _Nullable wcstok(wchar_t* _Nullable __s, const wchar_t* _Nonnull __delimiter, wchar_t* _Nonnull * _Nonnull __ptr);
+long wcstol(const wchar_t* _Nonnull __s, wchar_t* __BIONIC_COMPLICATED_NULLNESS * _Nullable __end_ptr, int __base);
+long wcstol_l(const wchar_t* _Nonnull __s, wchar_t* __BIONIC_COMPLICATED_NULLNESS * _Nullable __end_ptr, int __base, locale_t _Nonnull __l) __INTRODUCED_IN(28);
+long long wcstoll(const wchar_t* _Nonnull __s, wchar_t* __BIONIC_COMPLICATED_NULLNESS * _Nullable __end_ptr, int __base) __INTRODUCED_IN(21);
+long double wcstold(const wchar_t* _Nonnull __s, wchar_t* __BIONIC_COMPLICATED_NULLNESS * _Nullable __end_ptr) __RENAME_LDBL(wcstod, 3, 21);
+unsigned long wcstoul(const wchar_t* _Nonnull __s, wchar_t* __BIONIC_COMPLICATED_NULLNESS * _Nullable __end_ptr, int __base);
+unsigned long wcstoul_l(const wchar_t* _Nonnull __s, wchar_t* __BIONIC_COMPLICATED_NULLNESS * _Nullable __end_ptr, int __base, locale_t _Nonnull __l) __INTRODUCED_IN(28);
+unsigned long long wcstoull(const wchar_t* _Nonnull __s, wchar_t* __BIONIC_COMPLICATED_NULLNESS * _Nullable __end_ptr, int __base) __INTRODUCED_IN(21);
+int wcswidth(const wchar_t* _Nonnull __s, size_t __n);
+size_t wcsxfrm(wchar_t* __BIONIC_COMPLICATED_NULLNESS __dst, const wchar_t* _Nonnull __src, size_t __n);
int wctob(wint_t __wc);
int wcwidth(wchar_t __wc);
-wchar_t* wmemchr(const wchar_t* __src, wchar_t __wc, size_t __n);
-int wmemcmp(const wchar_t* __lhs, const wchar_t* __rhs, size_t __n);
-wchar_t* wmemcpy(wchar_t* __dst, const wchar_t* __src, size_t __n);
+wchar_t* _Nullable wmemchr(const wchar_t* _Nonnull __src, wchar_t __wc, size_t __n);
+int wmemcmp(const wchar_t* _Nullable __lhs, const wchar_t* _Nullable __rhs, size_t __n);
+wchar_t* _Nonnull wmemcpy(wchar_t* _Nonnull __dst, const wchar_t* _Nonnull __src, size_t __n);
#if defined(__USE_GNU)
-wchar_t* wmempcpy(wchar_t* __dst, const wchar_t* __src, size_t __n) __INTRODUCED_IN(23);
+wchar_t* _Nonnull wmempcpy(wchar_t* _Nonnull __dst, const wchar_t* _Nonnull __src, size_t __n) __INTRODUCED_IN(23);
#endif
-wchar_t* wmemmove(wchar_t* __dst, const wchar_t* __src, size_t __n);
-wchar_t* wmemset(wchar_t* __dst, wchar_t __wc, size_t __n);
-int wprintf(const wchar_t* __fmt, ...);
-int wscanf(const wchar_t* __fmt, ...);
+wchar_t* _Nonnull wmemmove(wchar_t* _Nonnull __dst, const wchar_t* _Nonnull __src, size_t __n);
+wchar_t* _Nonnull wmemset(wchar_t* _Nonnull __dst, wchar_t __wc, size_t __n);
+int wprintf(const wchar_t* _Nonnull __fmt, ...);
+int wscanf(const wchar_t* _Nonnull __fmt, ...);
-long long wcstoll_l(const wchar_t* __s, wchar_t** __end_ptr, int __base, locale_t __l) __INTRODUCED_IN(21);
-unsigned long long wcstoull_l(const wchar_t* __s, wchar_t** __end_ptr, int __base, locale_t __l) __INTRODUCED_IN(21);
-long double wcstold_l(const wchar_t* __s, wchar_t** __end_ptr, locale_t __l) __INTRODUCED_IN(21);
+long long wcstoll_l(const wchar_t* _Nonnull __s, wchar_t* _Nullable * _Nullable __end_ptr, int __base, locale_t _Nonnull __l) __INTRODUCED_IN(21);
+unsigned long long wcstoull_l(const wchar_t* _Nonnull __s, wchar_t* _Nullable * _Nullable __end_ptr, int __base, locale_t _Nonnull __l) __INTRODUCED_IN(21);
+long double wcstold_l(const wchar_t* _Nonnull __s, wchar_t* _Nullable * _Nullable __end_ptr, locale_t _Nonnull __l) __INTRODUCED_IN(21);
-int wcscoll_l(const wchar_t* __lhs, const wchar_t* __rhs, locale_t __l) __attribute_pure__
+int wcscoll_l(const wchar_t* _Nonnull __lhs, const wchar_t* _Nonnull __rhs, locale_t _Nonnull __l) __attribute_pure__
__INTRODUCED_IN(21);
-size_t wcsxfrm_l(wchar_t* __dst, const wchar_t* __src, size_t __n, locale_t __l) __INTRODUCED_IN(21);
+size_t wcsxfrm_l(wchar_t* __BIONIC_COMPLICATED_NULLNESS __dst, const wchar_t* _Nonnull __src, size_t __n, locale_t _Nonnull __l) __INTRODUCED_IN(21);
+size_t wcslcat(wchar_t* _Nonnull __dst, const wchar_t* _Nonnull __src, size_t __n);
+size_t wcslcpy(wchar_t* _Nonnull __dst, const wchar_t* _Nonnull __src, size_t __n);
-size_t wcslcat(wchar_t* __dst, const wchar_t* __src, size_t __n);
-size_t wcslcpy(wchar_t* __dst, const wchar_t* __src, size_t __n);
-
-FILE* open_wmemstream(wchar_t** __ptr, size_t* __size_ptr) __INTRODUCED_IN(23);
-wchar_t* wcsdup(const wchar_t* __s);
-size_t wcsnlen(const wchar_t* __s, size_t __n);
+FILE* _Nullable open_wmemstream(wchar_t* _Nonnull * _Nonnull __ptr, size_t* _Nonnull __size_ptr) __INTRODUCED_IN(23);
+wchar_t* _Nullable wcsdup(const wchar_t* _Nonnull __s);
+size_t wcsnlen(const wchar_t* _Nonnull __s, size_t __n);
__END_DECLS
diff --git a/libc/stdio/printf_common.h b/libc/stdio/printf_common.h
index e761835..b0055f0 100644
--- a/libc/stdio/printf_common.h
+++ b/libc/stdio/printf_common.h
@@ -528,6 +528,17 @@
case 'b':
ADDUARG();
break;
+ case 'w':
+ n = 0;
+ ch = *fmt++;
+ while (is_digit(ch)) {
+ APPEND_DIGIT(n, ch);
+ ch = *fmt++;
+ }
+ if (n == 64) {
+ flags |= LLONGINT;
+ }
+ goto reswitch;
default: /* "%?" prints ?, unless ? is NUL */
if (ch == '\0') goto done;
break;
diff --git a/libc/stdio/vfprintf.cpp b/libc/stdio/vfprintf.cpp
index d83a5bf..b7c68dd 100644
--- a/libc/stdio/vfprintf.cpp
+++ b/libc/stdio/vfprintf.cpp
@@ -521,6 +521,34 @@
_umax = UARG();
base = DEC;
goto nosign;
+ case 'w':
+ n = 0;
+ ch = *fmt++;
+ while (is_digit(ch)) {
+ APPEND_DIGIT(n, ch);
+ ch = *fmt++;
+ }
+ switch (n) {
+ case 8: {
+ flags |= CHARINT;
+ goto reswitch;
+ }
+ case 16: {
+ flags |= SHORTINT;
+ goto reswitch;
+ }
+ case 32: {
+ goto reswitch;
+ }
+ case 64: {
+ flags |= LLONGINT;
+ goto reswitch;
+ }
+ default: {
+ __fortify_fatal("%%w%d is unsupported", n);
+ break;
+ }
+ }
case 'X':
xdigs = xdigs_upper;
goto hex;
diff --git a/libc/stdio/vfwprintf.cpp b/libc/stdio/vfwprintf.cpp
index 9819a73..52ae64b 100644
--- a/libc/stdio/vfwprintf.cpp
+++ b/libc/stdio/vfwprintf.cpp
@@ -510,6 +510,34 @@
_umax = UARG();
base = DEC;
goto nosign;
+ case 'w':
+ n = 0;
+ ch = *fmt++;
+ while (is_digit(ch)) {
+ APPEND_DIGIT(n, ch);
+ ch = *fmt++;
+ }
+ switch (n) {
+ case 8: {
+ flags |= CHARINT;
+ goto reswitch;
+ }
+ case 16: {
+ flags |= SHORTINT;
+ goto reswitch;
+ }
+ case 32: {
+ goto reswitch;
+ }
+ case 64: {
+ flags |= LLONGINT;
+ goto reswitch;
+ }
+ default: {
+ __fortify_fatal("%%w%d is unsupported", n);
+ break;
+ }
+ }
case 'X':
xdigs = xdigs_upper;
goto hex;
diff --git a/linker/Android.bp b/linker/Android.bp
index 83077c6..020bd7d 100644
--- a/linker/Android.bp
+++ b/linker/Android.bp
@@ -374,6 +374,11 @@
],
symlinks: ["linker_asan"],
+ arch: {
+ arm64: {
+ symlinks: ["linker_hwasan"],
+ },
+ },
multilib: {
lib64: {
suffix: "64",
diff --git a/linker/linker.cpp b/linker/linker.cpp
index c5a822a..17b574f 100644
--- a/linker/linker.cpp
+++ b/linker/linker.cpp
@@ -34,6 +34,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <sys/auxv.h>
#include <sys/mman.h>
#include <sys/param.h>
#include <sys/vfs.h>
@@ -133,6 +134,36 @@
nullptr
};
+#if defined(__aarch64__)
+static const char* const kHwasanSystemLibDir = "/system/lib64/hwasan";
+static const char* const kHwasanOdmLibDir = "/odm/lib64/hwasan";
+static const char* const kHwasanVendorLibDir = "/vendor/lib64/hwasan";
+
+// HWASan is only supported on aarch64.
+static const char* const kHwsanDefaultLdPaths[] = {
+ kHwasanSystemLibDir,
+ kSystemLibDir,
+ kHwasanOdmLibDir,
+ kOdmLibDir,
+ kHwasanVendorLibDir,
+ kVendorLibDir,
+ nullptr
+};
+
+// Is HWASAN enabled?
+static bool g_is_hwasan = false;
+#else
+static const char* const kHwsanDefaultLdPaths[] = {
+ kSystemLibDir,
+ kOdmLibDir,
+ kVendorLibDir,
+ nullptr
+};
+
+// Never any HWASan. Help the compiler remove the code we don't need.
+constexpr bool g_is_hwasan = false;
+#endif
+
// Is ASAN enabled?
static bool g_is_asan = false;
@@ -2134,26 +2165,46 @@
}
// End Workaround for dlopen(/system/lib/<soname>) when .so is in /apex.
- std::string asan_name_holder;
+ std::string translated_name_holder;
+ assert(!g_is_hwasan || !g_is_asan);
const char* translated_name = name;
if (g_is_asan && translated_name != nullptr && translated_name[0] == '/') {
char original_path[PATH_MAX];
if (realpath(name, original_path) != nullptr) {
- asan_name_holder = std::string(kAsanLibDirPrefix) + original_path;
- if (file_exists(asan_name_holder.c_str())) {
+ translated_name_holder = std::string(kAsanLibDirPrefix) + original_path;
+ if (file_exists(translated_name_holder.c_str())) {
soinfo* si = nullptr;
if (find_loaded_library_by_realpath(ns, original_path, true, &si)) {
PRINT("linker_asan dlopen NOT translating \"%s\" -> \"%s\": library already loaded", name,
- asan_name_holder.c_str());
+ translated_name_holder.c_str());
} else {
PRINT("linker_asan dlopen translating \"%s\" -> \"%s\"", name, translated_name);
- translated_name = asan_name_holder.c_str();
+ translated_name = translated_name_holder.c_str();
+ }
+ }
+ }
+ } else if (g_is_hwasan && translated_name != nullptr && translated_name[0] == '/') {
+ char original_path[PATH_MAX];
+ if (realpath(name, original_path) != nullptr) {
+ // Keep this the same as CreateHwasanPath in system/linkerconfig/modules/namespace.cc.
+ std::string path(original_path);
+ auto slash = path.rfind('/');
+ if (slash != std::string::npos || slash != path.size() - 1) {
+ translated_name_holder = path.substr(0, slash) + "/hwasan" + path.substr(slash);
+ }
+ if (!translated_name_holder.empty() && file_exists(translated_name_holder.c_str())) {
+ soinfo* si = nullptr;
+ if (find_loaded_library_by_realpath(ns, original_path, true, &si)) {
+ PRINT("linker_hwasan dlopen NOT translating \"%s\" -> \"%s\": library already loaded", name,
+ translated_name_holder.c_str());
+ } else {
+ PRINT("linker_hwasan dlopen translating \"%s\" -> \"%s\"", name, translated_name);
+ translated_name = translated_name_holder.c_str();
}
}
}
}
-
ProtectedDataGuard guard;
soinfo* si = find_library(ns, translated_name, flags, extinfo, caller);
loading_trace.End();
@@ -3335,9 +3386,10 @@
return true;
}
-static std::vector<android_namespace_t*> init_default_namespace_no_config(bool is_asan) {
+static std::vector<android_namespace_t*> init_default_namespace_no_config(bool is_asan, bool is_hwasan) {
g_default_namespace.set_isolated(false);
- auto default_ld_paths = is_asan ? kAsanDefaultLdPaths : kDefaultLdPaths;
+ auto default_ld_paths = is_asan ? kAsanDefaultLdPaths : (
+ is_hwasan ? kHwsanDefaultLdPaths : kDefaultLdPaths);
char real_path[PATH_MAX];
std::vector<std::string> ld_default_paths;
@@ -3441,6 +3493,7 @@
return kLdConfigFilePath;
}
+
std::vector<android_namespace_t*> init_default_namespaces(const char* executable_path) {
g_default_namespace.set_name("(default)");
@@ -3454,6 +3507,16 @@
(strcmp(bname, "linker_asan") == 0 ||
strcmp(bname, "linker_asan64") == 0);
+#if defined(__aarch64__)
+ // HWASan is only supported on AArch64.
+ // The AT_SECURE restriction is because this is a debug feature that does
+ // not need to work on secure binaries, it doesn't hurt to disallow the
+ // environment variable for them, as it impacts the program execution.
+ char* hwasan_env = getenv("LD_HWASAN");
+ g_is_hwasan = (bname != nullptr &&
+ strcmp(bname, "linker_hwasan64") == 0) ||
+ (hwasan_env != nullptr && !getauxval(AT_SECURE) && strcmp(hwasan_env, "1") == 0);
+#endif
const Config* config = nullptr;
{
@@ -3461,7 +3524,7 @@
INFO("[ Reading linker config \"%s\" ]", ld_config_file_path.c_str());
ScopedTrace trace(("linker config " + ld_config_file_path).c_str());
std::string error_msg;
- if (!Config::read_binary_config(ld_config_file_path.c_str(), executable_path, g_is_asan,
+ if (!Config::read_binary_config(ld_config_file_path.c_str(), executable_path, g_is_asan, g_is_hwasan,
&config, &error_msg)) {
if (!error_msg.empty()) {
DL_WARN("Warning: couldn't read '%s' for '%s' (using default configuration instead): %s",
@@ -3472,7 +3535,7 @@
}
if (config == nullptr) {
- return init_default_namespace_no_config(g_is_asan);
+ return init_default_namespace_no_config(g_is_asan, g_is_hwasan);
}
const auto& namespace_configs = config->namespace_configs();
diff --git a/linker/linker_config.cpp b/linker/linker_config.cpp
index 1771e87..ad40c50 100644
--- a/linker/linker_config.cpp
+++ b/linker/linker_config.cpp
@@ -463,6 +463,7 @@
bool Config::read_binary_config(const char* ld_config_file_path,
const char* binary_realpath,
bool is_asan,
+ bool is_hwasan,
const Config** config,
std::string* error_msg) {
g_config.clear();
@@ -579,6 +580,8 @@
// these are affected by is_asan flag
if (is_asan) {
property_name_prefix += ".asan";
+ } else if (is_hwasan) {
+ property_name_prefix += ".hwasan";
}
// search paths are resolved (canonicalized). This is required mainly for
diff --git a/linker/linker_config.h b/linker/linker_config.h
index fe23ec1..09fea45 100644
--- a/linker/linker_config.h
+++ b/linker/linker_config.h
@@ -166,6 +166,7 @@
static bool read_binary_config(const char* ld_config_file_path,
const char* binary_realpath,
bool is_asan,
+ bool is_hwasan,
const Config** config,
std::string* error_msg);
diff --git a/linker/linker_config_test.cpp b/linker/linker_config_test.cpp
index acdf641..7e947f3 100644
--- a/linker/linker_config_test.cpp
+++ b/linker/linker_config_test.cpp
@@ -40,6 +40,7 @@
#include <android-base/file.h>
#include <android-base/scopeguard.h>
#include <android-base/stringprintf.h>
+#include <vector>
#if defined(__LP64__)
#define ARCH_SUFFIX "64"
@@ -64,6 +65,10 @@
"namespace.default.asan.search.paths = /data\n"
"namespace.default.asan.search.paths += /vendor/${LIB}\n"
"namespace.default.asan.permitted.paths = /data:/vendor\n"
+ "namespace.default.hwasan.search.paths = /vendor/${LIB}/hwasan\n"
+ "namespace.default.hwasan.search.paths += /vendor/${LIB}\n"
+ "namespace.default.hwasan.permitted.paths = /vendor/${LIB}/hwasan\n"
+ "namespace.default.hwasan.permitted.paths += /vendor/${LIB}\n"
"namespace.default.links = system\n"
"namespace.default.links += vndk\n"
// irregular whitespaces are added intentionally for testing purpose
@@ -77,11 +82,17 @@
"namespace.system.permitted.paths = /system/${LIB}\n"
"namespace.system.asan.search.paths = /data:/system/${LIB}\n"
"namespace.system.asan.permitted.paths = /data:/system\n"
+ "namespace.system.hwasan.search.paths = /system/${LIB}/hwasan\n"
+ "namespace.system.hwasan.search.paths += /system/${LIB}\n"
+ "namespace.system.hwasan.permitted.paths = /system/${LIB}/hwasan\n"
+ "namespace.system.hwasan.permitted.paths += /system/${LIB}\n"
"namespace.vndk.isolated = tr\n"
"namespace.vndk.isolated += ue\n" // should be ignored and return as 'false'.
"namespace.vndk.search.paths = /system/${LIB}/vndk\n"
"namespace.vndk.asan.search.paths = /data\n"
"namespace.vndk.asan.search.paths += /system/${LIB}/vndk\n"
+ "namespace.vndk.hwasan.search.paths = /system/${LIB}/vndk/hwasan\n"
+ "namespace.vndk.hwasan.search.paths += /system/${LIB}/vndk\n"
"namespace.vndk.links = default\n"
"namespace.vndk.link.default.allow_all_shared_libs = true\n"
"namespace.vndk.link.vndk_in_system.allow_all_shared_libs = true\n"
@@ -107,26 +118,50 @@
return resolved_paths;
}
-static void run_linker_config_smoke_test(bool is_asan) {
- const std::vector<std::string> kExpectedDefaultSearchPath =
- resolve_paths(is_asan ? std::vector<std::string>({ "/data", "/vendor/lib" ARCH_SUFFIX }) :
- std::vector<std::string>({ "/vendor/lib" ARCH_SUFFIX }));
+enum class SmokeTestType {
+ None,
+ Asan,
+ Hwasan,
+};
- const std::vector<std::string> kExpectedDefaultPermittedPath =
- resolve_paths(is_asan ? std::vector<std::string>({ "/data", "/vendor" }) :
- std::vector<std::string>({ "/vendor/lib" ARCH_SUFFIX }));
+static void run_linker_config_smoke_test(SmokeTestType type) {
+ std::vector<std::string> expected_default_search_path;
+ std::vector<std::string> expected_default_permitted_path;
+ std::vector<std::string> expected_system_search_path;
+ std::vector<std::string> expected_system_permitted_path;
+ std::vector<std::string> expected_vndk_search_path;
- const std::vector<std::string> kExpectedSystemSearchPath =
- resolve_paths(is_asan ? std::vector<std::string>({ "/data", "/system/lib" ARCH_SUFFIX }) :
- std::vector<std::string>({ "/system/lib" ARCH_SUFFIX }));
+ switch (type) {
+ case SmokeTestType::None:
+ expected_default_search_path = { "/vendor/lib" ARCH_SUFFIX };
+ expected_default_permitted_path = { "/vendor/lib" ARCH_SUFFIX };
+ expected_system_search_path = { "/system/lib" ARCH_SUFFIX };
+ expected_system_permitted_path = { "/system/lib" ARCH_SUFFIX };
+ expected_vndk_search_path = { "/system/lib" ARCH_SUFFIX "/vndk" };
+ break;
+ case SmokeTestType::Asan:
+ expected_default_search_path = { "/data", "/vendor/lib" ARCH_SUFFIX };
+ expected_default_permitted_path = { "/data", "/vendor" };
+ expected_system_search_path = { "/data", "/system/lib" ARCH_SUFFIX };
+ expected_system_permitted_path = { "/data", "/system" };
+ expected_vndk_search_path = { "/data", "/system/lib" ARCH_SUFFIX "/vndk" };
+ break;
+ case SmokeTestType::Hwasan:
+ expected_default_search_path = { "/vendor/lib" ARCH_SUFFIX "/hwasan", "/vendor/lib" ARCH_SUFFIX };
+ expected_default_permitted_path = { "/vendor/lib" ARCH_SUFFIX "/hwasan", "/vendor/lib" ARCH_SUFFIX };
+ expected_system_search_path = { "/system/lib" ARCH_SUFFIX "/hwasan" , "/system/lib" ARCH_SUFFIX };
+ expected_system_permitted_path = { "/system/lib" ARCH_SUFFIX "/hwasan", "/system/lib" ARCH_SUFFIX };
+ expected_vndk_search_path = { "/system/lib" ARCH_SUFFIX "/vndk/hwasan", "/system/lib" ARCH_SUFFIX "/vndk" };
+ break;
+ }
- const std::vector<std::string> kExpectedSystemPermittedPath =
- resolve_paths(is_asan ? std::vector<std::string>({ "/data", "/system" }) :
- std::vector<std::string>({ "/system/lib" ARCH_SUFFIX }));
-
- const std::vector<std::string> kExpectedVndkSearchPath =
- resolve_paths(is_asan ? std::vector<std::string>({ "/data", "/system/lib" ARCH_SUFFIX "/vndk"}) :
- std::vector<std::string>({ "/system/lib" ARCH_SUFFIX "/vndk"}));
+ expected_default_search_path = resolve_paths(expected_default_search_path);
+ // expected_default_permitted_path is skipped on purpose, permitted paths
+ // do not get resolved in linker_config.cpp
+ expected_system_search_path = resolve_paths(expected_system_search_path);
+ // expected_system_permitted_path is skipped on purpose, permitted paths
+ // do not get resolved in linker_config.cpp
+ expected_vndk_search_path = resolve_paths(expected_vndk_search_path);
TemporaryFile tmp_file;
close(tmp_file.fd);
@@ -149,7 +184,8 @@
std::string error_msg;
ASSERT_TRUE(Config::read_binary_config(tmp_file.path,
executable_path.c_str(),
- is_asan,
+ type == SmokeTestType::Asan,
+ type == SmokeTestType::Hwasan,
&config,
&error_msg)) << error_msg;
ASSERT_TRUE(config != nullptr);
@@ -162,8 +198,8 @@
ASSERT_TRUE(default_ns_config->isolated());
ASSERT_FALSE(default_ns_config->visible());
- ASSERT_EQ(kExpectedDefaultSearchPath, default_ns_config->search_paths());
- ASSERT_EQ(kExpectedDefaultPermittedPath, default_ns_config->permitted_paths());
+ ASSERT_EQ(expected_default_search_path, default_ns_config->search_paths());
+ ASSERT_EQ(expected_default_permitted_path, default_ns_config->permitted_paths());
const auto& default_ns_links = default_ns_config->links();
ASSERT_EQ(2U, default_ns_links.size());
@@ -202,14 +238,14 @@
ASSERT_TRUE(ns_system->isolated());
ASSERT_TRUE(ns_system->visible());
- ASSERT_EQ(kExpectedSystemSearchPath, ns_system->search_paths());
- ASSERT_EQ(kExpectedSystemPermittedPath, ns_system->permitted_paths());
+ ASSERT_EQ(expected_system_search_path, ns_system->search_paths());
+ ASSERT_EQ(expected_system_permitted_path, ns_system->permitted_paths());
ASSERT_TRUE(ns_vndk != nullptr) << "vndk namespace was not found";
ASSERT_FALSE(ns_vndk->isolated()); // malformed bool property
ASSERT_FALSE(ns_vndk->visible()); // undefined bool property
- ASSERT_EQ(kExpectedVndkSearchPath, ns_vndk->search_paths());
+ ASSERT_EQ(expected_vndk_search_path, ns_vndk->search_paths());
const auto& ns_vndk_links = ns_vndk->links();
ASSERT_EQ(1U, ns_vndk_links.size());
@@ -223,11 +259,15 @@
}
TEST(linker_config, smoke) {
- run_linker_config_smoke_test(false);
+ run_linker_config_smoke_test(SmokeTestType::None);
}
TEST(linker_config, asan_smoke) {
- run_linker_config_smoke_test(true);
+ run_linker_config_smoke_test(SmokeTestType::Asan);
+}
+
+TEST(linker_config, hwasan_smoke) {
+ run_linker_config_smoke_test(SmokeTestType::Hwasan);
}
TEST(linker_config, ns_link_shared_libs_invalid_settings) {
@@ -259,6 +299,7 @@
ASSERT_FALSE(Config::read_binary_config(tmp_file.path,
executable_path.c_str(),
false,
+ false,
&config,
&error_msg));
ASSERT_TRUE(config == nullptr);
@@ -304,6 +345,7 @@
ASSERT_TRUE(Config::read_binary_config(tmp_file.path,
executable_path.c_str(),
false,
+ false,
&config,
&error_msg)) << error_msg;
diff --git a/tests/Android.bp b/tests/Android.bp
index 1949079..1be1ec3 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -541,6 +541,10 @@
// unsupported relocation type 37
"ifunc_test.cpp",
+
+ // musl #defines utmp to utmpx, causing a collision with
+ // utmpx_test.cpp
+ "utmp_test.cpp",
],
},
},
@@ -1112,6 +1116,30 @@
}
cc_test {
+ name: "hwasan_test",
+ enabled: false,
+ // This does not use bionic_tests_defaults because it is not supported on
+ // host.
+ arch: {
+ arm64: {
+ enabled: true,
+ },
+ },
+ sanitize: {
+ hwaddress: true,
+ },
+ srcs: [
+ "hwasan_test.cpp",
+ ],
+ shared_libs: [
+ "libbase",
+ ],
+ data_libs: ["libtest_simple_hwasan", "libtest_simple_hwasan_nohwasan"],
+ header_libs: ["bionic_libc_platform_headers"],
+ test_suites: ["device-tests"],
+}
+
+cc_test {
name: "bionic-stress-tests",
defaults: [
"bionic_tests_defaults",
diff --git a/tests/dlfcn_test.cpp b/tests/dlfcn_test.cpp
index 3f70279..b68ee7b 100644
--- a/tests/dlfcn_test.cpp
+++ b/tests/dlfcn_test.cpp
@@ -971,9 +971,15 @@
}
#define ALTERNATE_PATH_TO_SYSTEM_LIB "/system/lib64/" ABI_STRING "/"
+#if __has_feature(hwaddress_sanitizer)
+#define PATH_TO_LIBC PATH_TO_SYSTEM_LIB "hwasan/libc.so"
+#define PATH_TO_BOOTSTRAP_LIBC PATH_TO_SYSTEM_LIB "bootstrap/hwasan/libc.so"
+#define ALTERNATE_PATH_TO_LIBC ALTERNATE_PATH_TO_SYSTEM_LIB "hwasan/libc.so"
+#else
#define PATH_TO_LIBC PATH_TO_SYSTEM_LIB "libc.so"
#define PATH_TO_BOOTSTRAP_LIBC PATH_TO_SYSTEM_LIB "bootstrap/libc.so"
#define ALTERNATE_PATH_TO_LIBC ALTERNATE_PATH_TO_SYSTEM_LIB "libc.so"
+#endif
TEST(dlfcn, dladdr_libc) {
#if defined(__GLIBC__)
@@ -981,24 +987,25 @@
#endif
Dl_info info;
- void* addr = reinterpret_cast<void*>(puts); // well-known libc function
+ void* addr = reinterpret_cast<void*>(puts); // An arbitrary libc function.
ASSERT_TRUE(dladdr(addr, &info) != 0);
- char libc_realpath[PATH_MAX];
-
// Check if libc is in canonical path or in alternate path.
+ const char* expected_path;
if (strncmp(ALTERNATE_PATH_TO_SYSTEM_LIB,
info.dli_fname,
sizeof(ALTERNATE_PATH_TO_SYSTEM_LIB) - 1) == 0) {
// Platform with emulated architecture. Symlink on ARC++.
- ASSERT_TRUE(realpath(ALTERNATE_PATH_TO_LIBC, libc_realpath) == libc_realpath);
+ expected_path = ALTERNATE_PATH_TO_LIBC;
} else if (strncmp(PATH_TO_BOOTSTRAP_LIBC, info.dli_fname,
sizeof(PATH_TO_BOOTSTRAP_LIBC) - 1) == 0) {
- ASSERT_TRUE(realpath(PATH_TO_BOOTSTRAP_LIBC, libc_realpath) == libc_realpath);
+ expected_path = PATH_TO_BOOTSTRAP_LIBC;
} else {
// /system/lib is symlink when this test is executed on host.
- ASSERT_TRUE(realpath(PATH_TO_LIBC, libc_realpath) == libc_realpath);
+ expected_path = PATH_TO_LIBC;
}
+ char libc_realpath[PATH_MAX];
+ ASSERT_TRUE(realpath(expected_path, libc_realpath) != nullptr) << strerror(errno);
ASSERT_STREQ(libc_realpath, info.dli_fname);
// TODO: add check for dfi_fbase
diff --git a/tests/hwasan_test.cpp b/tests/hwasan_test.cpp
new file mode 100644
index 0000000..5c21495
--- /dev/null
+++ b/tests/hwasan_test.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2023 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 <dlfcn.h>
+#include <stdlib.h>
+
+#include <gtest/gtest.h>
+
+#include <android-base/silent_death_test.h>
+#include <android-base/test_utils.h>
+
+using HwasanDeathTest = SilentDeathTest;
+
+TEST_F(HwasanDeathTest, UseAfterFree) {
+ EXPECT_DEATH(
+ {
+ void* m = malloc(1);
+ volatile char* x = const_cast<volatile char*>(reinterpret_cast<char*>(m));
+ *x = 1;
+ free(m);
+ *x = 2;
+ },
+ "use-after-free");
+}
+
+TEST_F(HwasanDeathTest, OutOfBounds) {
+ EXPECT_DEATH(
+ {
+ void* m = malloc(1);
+ volatile char* x = const_cast<volatile char*>(reinterpret_cast<char*>(m));
+ x[1] = 1;
+ },
+ "buffer-overflow");
+}
+
+// Check whether dlopen of /foo/bar.so checks /foo/hwasan/bar.so first.
+TEST(HwasanTest, DlopenAbsolutePath) {
+ std::string path = android::base::GetExecutableDirectory() + "/libtest_simple_hwasan.so";
+ ASSERT_EQ(0, access(path.c_str(), F_OK)); // Verify test setup.
+ std::string hwasan_path =
+ android::base::GetExecutableDirectory() + "/hwasan/libtest_simple_hwasan.so";
+ ASSERT_EQ(0, access(hwasan_path.c_str(), F_OK)); // Verify test setup.
+
+ void* handle = dlopen(path.c_str(), RTLD_NOW);
+ ASSERT_TRUE(handle != nullptr);
+ uint32_t* compiled_with_hwasan =
+ reinterpret_cast<uint32_t*>(dlsym(handle, "dlopen_testlib_compiled_with_hwasan"));
+ EXPECT_TRUE(*compiled_with_hwasan);
+ dlclose(handle);
+}
+
+TEST(HwasanTest, IsRunningWithHWasan) {
+ EXPECT_TRUE(running_with_hwasan());
+}
diff --git a/tests/libs/Android.bp b/tests/libs/Android.bp
index 8ae2257..a2fbe55 100644
--- a/tests/libs/Android.bp
+++ b/tests/libs/Android.bp
@@ -243,6 +243,38 @@
}
// -----------------------------------------------------------------------------
+// Libraries used by hwasan_test
+// -----------------------------------------------------------------------------
+cc_test_library {
+ name: "libtest_simple_hwasan",
+ arch: {
+ arm64: {
+ enabled: true,
+ },
+ },
+ sanitize: {
+ hwaddress: true,
+ },
+ relative_install_path: "hwasan",
+ enabled: false,
+ srcs: ["dlopen_testlib_simple_hwasan.cpp"],
+}
+
+cc_test_library {
+ // A weird name. This is the vanilla (non-HWASan) copy of the library that
+ // is used for the hwasan test.
+ name: "libtest_simple_hwasan_nohwasan",
+ arch: {
+ arm64: {
+ enabled: true,
+ },
+ },
+ stem: "libtest_simple_hwasan",
+ enabled: false,
+ srcs: ["dlopen_testlib_simple_hwasan.cpp"],
+}
+
+// -----------------------------------------------------------------------------
// Library used by dlext direct unload on the namespace boundary tests
// -----------------------------------------------------------------------------
cc_test_library {
diff --git a/tests/libs/dlopen_testlib_simple_hwasan.cpp b/tests/libs/dlopen_testlib_simple_hwasan.cpp
new file mode 100644
index 0000000..b92e05f
--- /dev/null
+++ b/tests/libs/dlopen_testlib_simple_hwasan.cpp
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2023 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 <stdint.h>
+
+#if __has_feature(hwaddress_sanitizer)
+extern "C" uint32_t dlopen_testlib_compiled_with_hwasan = true;
+#else
+extern "C" uint32_t dlopen_testlib_compiled_with_hwasan = false;
+#endif
+
+extern "C" bool dlopen_testlib_simple_hwasan_func() {
+ return true;
+}
diff --git a/tests/pthread_test.cpp b/tests/pthread_test.cpp
index 06a0f3d..aad2a4d 100644
--- a/tests/pthread_test.cpp
+++ b/tests/pthread_test.cpp
@@ -195,12 +195,12 @@
SKIP_WITH_HWASAN; // TODO(b/148982147): Re-enable when fixed.
size_t stack_size = 640 * 1024;
- std::vector<char> stack_vec(stack_size, '\xff');
- void* stack = stack_vec.data();
+ std::unique_ptr<char[]> stack(new (std::align_val_t(getpagesize())) char[stack_size]);
+ memset(stack.get(), '\xff', stack_size);
pthread_attr_t attr;
ASSERT_EQ(0, pthread_attr_init(&attr));
- ASSERT_EQ(0, pthread_attr_setstack(&attr, stack, stack_size));
+ ASSERT_EQ(0, pthread_attr_setstack(&attr, stack.get(), stack_size));
pthread_t t;
ASSERT_EQ(0, pthread_create(&t, &attr, FnWithStackFrame, nullptr));
diff --git a/tests/stdio_test.cpp b/tests/stdio_test.cpp
index fb6bce9..0e267c5 100644
--- a/tests/stdio_test.cpp
+++ b/tests/stdio_test.cpp
@@ -36,6 +36,7 @@
#include <android-base/file.h>
#include <android-base/silent_death_test.h>
+#include <android-base/strings.h>
#include <android-base/test_utils.h>
#include <android-base/unique_fd.h>
@@ -140,6 +141,22 @@
fclose(fp);
}
+TEST(STDIO_TEST, tmpfile_TMPDIR) {
+ TemporaryDir td;
+ setenv("TMPDIR", td.path, 1);
+
+ FILE* fp = tmpfile();
+ ASSERT_TRUE(fp != nullptr);
+
+ std::string fd_path = android::base::StringPrintf("/proc/self/fd/%d", fileno(fp));
+ char path[PATH_MAX];
+ ASSERT_GT(readlink(fd_path.c_str(), path, sizeof(path)), 0);
+ // $TMPDIR influenced where our temporary file ended up?
+ ASSERT_TRUE(android::base::StartsWith(path, td.path)) << path;
+ // And we used O_TMPFILE, right?
+ ASSERT_TRUE(android::base::EndsWith(path, " (deleted)")) << path;
+}
+
TEST(STDIO_TEST, dprintf) {
TemporaryFile tf;
@@ -2984,59 +3001,153 @@
}
TEST(STDIO_TEST, snprintf_b) {
+#if defined(__BIONIC__)
char buf[BUFSIZ];
- EXPECT_EQ(5, snprintf(buf, sizeof(buf), "<%b>", 5));
+
+ uint8_t b = 5;
+ EXPECT_EQ(5, snprintf(buf, sizeof(buf), "<%" PRIb8 ">", b));
EXPECT_STREQ("<101>", buf);
- EXPECT_EQ(10, snprintf(buf, sizeof(buf), "<%08b>", 5));
+ EXPECT_EQ(10, snprintf(buf, sizeof(buf), "<%08" PRIb8 ">", b));
EXPECT_STREQ("<00000101>", buf);
- EXPECT_EQ(34, snprintf(buf, sizeof(buf), "<%b>", 0xaaaaaaaa));
+
+ uint16_t s = 0xaaaa;
+ EXPECT_EQ(18, snprintf(buf, sizeof(buf), "<%" PRIb16 ">", s));
+ EXPECT_STREQ("<1010101010101010>", buf);
+ EXPECT_EQ(20, snprintf(buf, sizeof(buf), "<%#" PRIb16 ">", s));
+ EXPECT_STREQ("<0b1010101010101010>", buf);
+
+ EXPECT_EQ(34, snprintf(buf, sizeof(buf), "<%" PRIb32 ">", 0xaaaaaaaa));
EXPECT_STREQ("<10101010101010101010101010101010>", buf);
- EXPECT_EQ(36, snprintf(buf, sizeof(buf), "<%#b>", 0xaaaaaaaa));
+ EXPECT_EQ(36, snprintf(buf, sizeof(buf), "<%#" PRIb32 ">", 0xaaaaaaaa));
EXPECT_STREQ("<0b10101010101010101010101010101010>", buf);
+
+ // clang doesn't like "%lb" (https://github.com/llvm/llvm-project/issues/62247)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wformat"
+ EXPECT_EQ(66, snprintf(buf, sizeof(buf), "<%" PRIb64 ">", 0xaaaaaaaa'aaaaaaaa));
+ EXPECT_STREQ("<1010101010101010101010101010101010101010101010101010101010101010>", buf);
+ EXPECT_EQ(68, snprintf(buf, sizeof(buf), "<%#" PRIb64 ">", 0xaaaaaaaa'aaaaaaaa));
+ EXPECT_STREQ("<0b1010101010101010101010101010101010101010101010101010101010101010>", buf);
+#pragma clang diagnostic pop
+
EXPECT_EQ(3, snprintf(buf, sizeof(buf), "<%#b>", 0));
EXPECT_STREQ("<0>", buf);
+#else
+ GTEST_SKIP() << "no %b in glibc";
+#endif
}
TEST(STDIO_TEST, snprintf_B) {
+#if defined(__BIONIC__)
char buf[BUFSIZ];
- EXPECT_EQ(5, snprintf(buf, sizeof(buf), "<%B>", 5));
+
+ uint8_t b = 5;
+ EXPECT_EQ(5, snprintf(buf, sizeof(buf), "<%" PRIB8 ">", b));
EXPECT_STREQ("<101>", buf);
- EXPECT_EQ(10, snprintf(buf, sizeof(buf), "<%08B>", 5));
+ EXPECT_EQ(10, snprintf(buf, sizeof(buf), "<%08" PRIB8 ">", b));
EXPECT_STREQ("<00000101>", buf);
- EXPECT_EQ(34, snprintf(buf, sizeof(buf), "<%B>", 0xaaaaaaaa));
+
+ uint16_t s = 0xaaaa;
+ EXPECT_EQ(18, snprintf(buf, sizeof(buf), "<%" PRIB16 ">", s));
+ EXPECT_STREQ("<1010101010101010>", buf);
+ EXPECT_EQ(20, snprintf(buf, sizeof(buf), "<%#" PRIB16 ">", s));
+ EXPECT_STREQ("<0B1010101010101010>", buf);
+
+ EXPECT_EQ(34, snprintf(buf, sizeof(buf), "<%" PRIB32 ">", 0xaaaaaaaa));
EXPECT_STREQ("<10101010101010101010101010101010>", buf);
- EXPECT_EQ(36, snprintf(buf, sizeof(buf), "<%#B>", 0xaaaaaaaa));
+ EXPECT_EQ(36, snprintf(buf, sizeof(buf), "<%#" PRIB32 ">", 0xaaaaaaaa));
EXPECT_STREQ("<0B10101010101010101010101010101010>", buf);
- EXPECT_EQ(3, snprintf(buf, sizeof(buf), "<%#B>", 0));
+
+ // clang doesn't like "%lB" (https://github.com/llvm/llvm-project/issues/62247)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wformat"
+ EXPECT_EQ(66, snprintf(buf, sizeof(buf), "<%" PRIB64 ">", 0xaaaaaaaa'aaaaaaaa));
+ EXPECT_STREQ("<1010101010101010101010101010101010101010101010101010101010101010>", buf);
+ EXPECT_EQ(68, snprintf(buf, sizeof(buf), "<%#" PRIB64 ">", 0xaaaaaaaa'aaaaaaaa));
+ EXPECT_STREQ("<0B1010101010101010101010101010101010101010101010101010101010101010>", buf);
+#pragma clang diagnostic pop
+
+ EXPECT_EQ(3, snprintf(buf, sizeof(buf), "<%#b>", 0));
EXPECT_STREQ("<0>", buf);
+#else
+ GTEST_SKIP() << "no %B in glibc";
+#endif
}
TEST(STDIO_TEST, swprintf_b) {
+#if defined(__BIONIC__)
wchar_t buf[BUFSIZ];
- EXPECT_EQ(5, swprintf(buf, sizeof(buf), L"<%b>", 5));
+
+ uint8_t b = 5;
+ EXPECT_EQ(5, swprintf(buf, sizeof(buf), L"<%" PRIb8 ">", b));
EXPECT_EQ(std::wstring(L"<101>"), buf);
- EXPECT_EQ(10, swprintf(buf, sizeof(buf), L"<%08b>", 5));
+ EXPECT_EQ(10, swprintf(buf, sizeof(buf), L"<%08" PRIb8 ">", b));
EXPECT_EQ(std::wstring(L"<00000101>"), buf);
- EXPECT_EQ(34, swprintf(buf, sizeof(buf), L"<%b>", 0xaaaaaaaa));
+
+ uint16_t s = 0xaaaa;
+ EXPECT_EQ(18, swprintf(buf, sizeof(buf), L"<%" PRIb16 ">", s));
+ EXPECT_EQ(std::wstring(L"<1010101010101010>"), buf);
+ EXPECT_EQ(20, swprintf(buf, sizeof(buf), L"<%#" PRIb16 ">", s));
+ EXPECT_EQ(std::wstring(L"<0b1010101010101010>"), buf);
+
+ EXPECT_EQ(34, swprintf(buf, sizeof(buf), L"<%" PRIb32 ">", 0xaaaaaaaa));
EXPECT_EQ(std::wstring(L"<10101010101010101010101010101010>"), buf);
- EXPECT_EQ(36, swprintf(buf, sizeof(buf), L"<%#b>", 0xaaaaaaaa));
+ EXPECT_EQ(36, swprintf(buf, sizeof(buf), L"<%#" PRIb32 ">", 0xaaaaaaaa));
EXPECT_EQ(std::wstring(L"<0b10101010101010101010101010101010>"), buf);
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wformat" // clang doesn't like "%lb"
+ EXPECT_EQ(66, swprintf(buf, sizeof(buf), L"<%" PRIb64 ">", 0xaaaaaaaa'aaaaaaaa));
+ EXPECT_EQ(std::wstring(L"<1010101010101010101010101010101010101010101010101010101010101010>"),
+ buf);
+ EXPECT_EQ(68, swprintf(buf, sizeof(buf), L"<%#" PRIb64 ">", 0xaaaaaaaa'aaaaaaaa));
+ EXPECT_EQ(std::wstring(L"<0b1010101010101010101010101010101010101010101010101010101010101010>"),
+ buf);
+#pragma clang diagnostic pop
+
EXPECT_EQ(3, swprintf(buf, sizeof(buf), L"<%#b>", 0));
EXPECT_EQ(std::wstring(L"<0>"), buf);
+#else
+ GTEST_SKIP() << "no %b in glibc";
+#endif
}
TEST(STDIO_TEST, swprintf_B) {
+#if defined(__BIONIC__)
wchar_t buf[BUFSIZ];
- EXPECT_EQ(5, swprintf(buf, sizeof(buf), L"<%B>", 5));
+
+ uint8_t b = 5;
+ EXPECT_EQ(5, swprintf(buf, sizeof(buf), L"<%" PRIB8 ">", b));
EXPECT_EQ(std::wstring(L"<101>"), buf);
- EXPECT_EQ(10, swprintf(buf, sizeof(buf), L"<%08B>", 5));
+ EXPECT_EQ(10, swprintf(buf, sizeof(buf), L"<%08" PRIB8 ">", b));
EXPECT_EQ(std::wstring(L"<00000101>"), buf);
- EXPECT_EQ(34, swprintf(buf, sizeof(buf), L"<%B>", 0xaaaaaaaa));
+
+ uint16_t s = 0xaaaa;
+ EXPECT_EQ(18, swprintf(buf, sizeof(buf), L"<%" PRIB16 ">", s));
+ EXPECT_EQ(std::wstring(L"<1010101010101010>"), buf);
+ EXPECT_EQ(20, swprintf(buf, sizeof(buf), L"<%#" PRIB16 ">", s));
+ EXPECT_EQ(std::wstring(L"<0B1010101010101010>"), buf);
+
+ EXPECT_EQ(34, swprintf(buf, sizeof(buf), L"<%" PRIB32 ">", 0xaaaaaaaa));
EXPECT_EQ(std::wstring(L"<10101010101010101010101010101010>"), buf);
- EXPECT_EQ(36, swprintf(buf, sizeof(buf), L"<%#B>", 0xaaaaaaaa));
+ EXPECT_EQ(36, swprintf(buf, sizeof(buf), L"<%#" PRIB32 ">", 0xaaaaaaaa));
EXPECT_EQ(std::wstring(L"<0B10101010101010101010101010101010>"), buf);
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wformat" // clang doesn't like "%lb"
+ EXPECT_EQ(66, swprintf(buf, sizeof(buf), L"<%" PRIB64 ">", 0xaaaaaaaa'aaaaaaaa));
+ EXPECT_EQ(std::wstring(L"<1010101010101010101010101010101010101010101010101010101010101010>"),
+ buf);
+ EXPECT_EQ(68, swprintf(buf, sizeof(buf), L"<%#" PRIB64 ">", 0xaaaaaaaa'aaaaaaaa));
+ EXPECT_EQ(std::wstring(L"<0B1010101010101010101010101010101010101010101010101010101010101010>"),
+ buf);
+#pragma clang diagnostic pop
+
EXPECT_EQ(3, swprintf(buf, sizeof(buf), L"<%#B>", 0));
EXPECT_EQ(std::wstring(L"<0>"), buf);
+#else
+ GTEST_SKIP() << "no %B in glibc";
+#endif
}
TEST(STDIO_TEST, scanf_i_decimal) {
@@ -3164,3 +3275,144 @@
EXPECT_EQ(0, i);
EXPECT_EQ('b', ch);
}
+
+TEST(STDIO_TEST, snprintf_w_base) {
+#if defined(__BIONIC__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wformat-invalid-specifier"
+#pragma clang diagnostic ignored "-Wconstant-conversion"
+ char buf[BUFSIZ];
+ int8_t a = 0b101;
+ snprintf(buf, sizeof(buf), "<%w8b>", a);
+ EXPECT_STREQ("<101>", buf);
+ int8_t b1 = 0xFF;
+ snprintf(buf, sizeof(buf), "<%w8d>", b1);
+ EXPECT_STREQ("<-1>", buf);
+ int8_t b2 = 0x1FF;
+ snprintf(buf, sizeof(buf), "<%w8d>", b2);
+ EXPECT_STREQ("<-1>", buf);
+ int16_t c = 0xFFFF;
+ snprintf(buf, sizeof(buf), "<%w16i>", c);
+ EXPECT_STREQ("<-1>", buf);
+ int32_t d = 021;
+ snprintf(buf, sizeof(buf), "<%w32o>", d);
+ EXPECT_STREQ("<21>", buf);
+ uint32_t e = -1;
+ snprintf(buf, sizeof(buf), "<%w32u>", e);
+ EXPECT_STREQ("<4294967295>", buf);
+ int64_t f = 0x3b;
+ snprintf(buf, sizeof(buf), "<%w64x>", f);
+ EXPECT_STREQ("<3b>", buf);
+ snprintf(buf, sizeof(buf), "<%w64X>", f);
+ EXPECT_STREQ("<3B>", buf);
+#pragma clang diagnostic pop
+#else
+ GTEST_SKIP() << "no %w in glibc";
+#endif
+}
+
+TEST(STDIO_TEST, snprintf_w_arguments_reordering) {
+#if defined(__BIONIC__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wformat-invalid-specifier"
+#pragma clang diagnostic ignored "-Wformat-extra-args"
+ char buf[BUFSIZ];
+ int32_t a = 0xaaaaaaaa;
+ int64_t b = 0x11111111'22222222;
+ int64_t c = 0x33333333'44444444;
+ int64_t d = 0xaaaaaaaa'aaaaaaaa;
+ snprintf(buf, sizeof(buf), "<%2$w32b --- %1$w64x>", c, a);
+ EXPECT_STREQ("<10101010101010101010101010101010 --- 3333333344444444>", buf);
+ snprintf(buf, sizeof(buf), "<%3$w64b --- %1$w64x --- %2$w64x>", b, c, d);
+ EXPECT_STREQ(
+ "<1010101010101010101010101010101010101010101010101010101010101010 --- 1111111122222222 --- "
+ "3333333344444444>",
+ buf);
+#pragma clang diagnostic pop
+#else
+ GTEST_SKIP() << "no %w in glibc";
+#endif
+}
+
+TEST(STDIO_TEST, snprintf_invalid_w_width) {
+#if defined(__BIONIC__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wformat-invalid-specifier"
+ char buf[BUFSIZ];
+ int32_t a = 100;
+ EXPECT_DEATH(snprintf(buf, sizeof(buf), "%w20d", &a), "%w20 is unsupported");
+#pragma clang diagnostic pop
+#else
+ GTEST_SKIP() << "no %w in glibc";
+#endif
+}
+
+TEST(STDIO_TEST, swprintf_w_base) {
+#if defined(__BIONIC__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wformat-invalid-specifier"
+#pragma clang diagnostic ignored "-Wconstant-conversion"
+ wchar_t buf[BUFSIZ];
+ int8_t a = 0b101;
+ swprintf(buf, sizeof(buf), L"<%w8b>", a);
+ EXPECT_EQ(std::wstring(L"<101>"), buf);
+ int8_t b1 = 0xFF;
+ swprintf(buf, sizeof(buf), L"<%w8d>", b1);
+ EXPECT_EQ(std::wstring(L"<-1>"), buf);
+ int8_t b2 = 0x1FF;
+ swprintf(buf, sizeof(buf), L"<%w8d>", b2);
+ EXPECT_EQ(std::wstring(L"<-1>"), buf);
+ int16_t c = 0xFFFF;
+ swprintf(buf, sizeof(buf), L"<%w16i>", c);
+ EXPECT_EQ(std::wstring(L"<-1>"), buf);
+ int32_t d = 021;
+ swprintf(buf, sizeof(buf), L"<%w32o>", d);
+ EXPECT_EQ(std::wstring(L"<21>"), buf);
+ uint32_t e = -1;
+ swprintf(buf, sizeof(buf), L"<%w32u>", e);
+ EXPECT_EQ(std::wstring(L"<4294967295>"), buf);
+ int64_t f = 0x3b;
+ swprintf(buf, sizeof(buf), L"<%w64x>", f);
+ EXPECT_EQ(std::wstring(L"<3b>"), buf);
+ swprintf(buf, sizeof(buf), L"<%w64X>", f);
+ EXPECT_EQ(std::wstring(L"<3B>"), buf);
+#pragma clang diagnostic pop
+#else
+ GTEST_SKIP() << "no %w in glibc";
+#endif
+}
+
+TEST(STDIO_TEST, swprintf_w_arguments_reordering) {
+#if defined(__BIONIC__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wformat-invalid-specifier"
+#pragma clang diagnostic ignored "-Wformat-extra-args"
+ wchar_t buf[BUFSIZ];
+ int32_t a = 0xaaaaaaaa;
+ int64_t b = 0x11111111'22222222;
+ int64_t c = 0x33333333'44444444;
+ int64_t d = 0xaaaaaaaa'aaaaaaaa;
+ swprintf(buf, sizeof(buf), L"<%2$w32b --- %1$w64x>", c, a);
+ EXPECT_EQ(std::wstring(L"<10101010101010101010101010101010 --- 3333333344444444>"), buf);
+ swprintf(buf, sizeof(buf), L"<%3$w64b --- %1$w64x --- %2$w64x>", b, c, d);
+ EXPECT_EQ(std::wstring(L"<1010101010101010101010101010101010101010101010101010101010101010 --- "
+ L"1111111122222222 --- 3333333344444444>"),
+ buf);
+#pragma clang diagnostic pop
+#else
+ GTEST_SKIP() << "no %w in glibc";
+#endif
+}
+
+TEST(STDIO_TEST, swprintf_invalid_w_width) {
+#if defined(__BIONIC__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wformat-invalid-specifier"
+ wchar_t buf[BUFSIZ];
+ int32_t a = 100;
+ EXPECT_DEATH(swprintf(buf, sizeof(buf), L"%w20d", &a), "%w20 is unsupported");
+#pragma clang diagnostic pop
+#else
+ GTEST_SKIP() << "no %w in glibc";
+#endif
+}
\ No newline at end of file
diff --git a/tests/sys_msg_test.cpp b/tests/sys_msg_test.cpp
index 200f654..da45087 100644
--- a/tests/sys_msg_test.cpp
+++ b/tests/sys_msg_test.cpp
@@ -74,9 +74,12 @@
}
TEST(sys_msg, msgctl_failure) {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wnonnull"
errno = 0;
ASSERT_EQ(-1, msgctl(-1, IPC_STAT, nullptr));
ASSERT_TRUE(errno == EINVAL || errno == ENOSYS);
+#pragma clang diagnostic pop
}
TEST(sys_msg, msgget_failure) {
@@ -86,9 +89,12 @@
}
TEST(sys_msg, msgrcv_failure) {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wnonnull"
errno = 0;
ASSERT_EQ(-1, msgrcv(-1, nullptr, 0, 0, 0));
ASSERT_TRUE(errno == EINVAL || errno == ENOSYS);
+#pragma clang diagnostic pop
}
TEST(sys_msg, msgsnd_failure) {
diff --git a/tests/sys_random_test.cpp b/tests/sys_random_test.cpp
index 2e2665b..e0cbf78 100644
--- a/tests/sys_random_test.cpp
+++ b/tests/sys_random_test.cpp
@@ -48,6 +48,8 @@
}
TEST(sys_random, getentropy_EFAULT) {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wnonnull"
#if defined(HAVE_SYS_RANDOM)
errno = 0;
ASSERT_EQ(-1, getentropy(nullptr, 1));
@@ -55,6 +57,7 @@
#else
GTEST_SKIP() << "<sys/random.h> not available";
#endif
+#pragma clang diagnostic pop
}
TEST(sys_random, getentropy_EIO) {
@@ -84,6 +87,8 @@
}
TEST(sys_random, getrandom_EFAULT) {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wnonnull"
#if defined(HAVE_SYS_RANDOM)
errno = 0;
ASSERT_EQ(-1, getrandom(nullptr, 256, 0));
@@ -91,6 +96,7 @@
#else
GTEST_SKIP() << "<sys/random.h> not available";
#endif
+#pragma clang diagnostic pop
}
TEST(sys_random, getrandom_EINVAL) {
diff --git a/tests/sys_sem_test.cpp b/tests/sys_sem_test.cpp
index b98926b..4bac92f 100644
--- a/tests/sys_sem_test.cpp
+++ b/tests/sys_sem_test.cpp
@@ -87,15 +87,21 @@
}
TEST(sys_sem, semop_failure) {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wnonnull"
errno = 0;
ASSERT_EQ(-1, semop(-1, nullptr, 0));
ASSERT_TRUE(errno == EINVAL || errno == ENOSYS);
+#pragma clang diagnostic pop
}
TEST(sys_sem, semtimedop_failure) {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wnonnull"
errno = 0;
ASSERT_EQ(-1, semtimedop(-1, nullptr, 0, nullptr));
ASSERT_TRUE(errno == EINVAL || errno == ENOSYS);
+#pragma clang diagnostic pop
}
TEST(sys_sem, union_semun) {
diff --git a/tests/wchar_test.cpp b/tests/wchar_test.cpp
index 16d4348..8716810 100644
--- a/tests/wchar_test.cpp
+++ b/tests/wchar_test.cpp
@@ -714,7 +714,8 @@
#if defined(__BIONIC__)
wchar_t* p;
size_t size;
-
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wnonnull"
// Invalid buffer.
errno = 0;
ASSERT_EQ(nullptr, open_wmemstream(nullptr, &size));
@@ -724,6 +725,7 @@
errno = 0;
ASSERT_EQ(nullptr, open_wmemstream(&p, nullptr));
ASSERT_EQ(EINVAL, errno);
+#pragma clang diagnostic pop
#else
GTEST_SKIP() << "This test is bionic-specific";
#endif