@@ -37,6 +37,20 @@ file, You can obtain one at https://mozilla.org/MPL/2.0/.
3737#include <linux/input.h>
3838#include <linux/input-event-codes.h>
3939
40+ /**
41+ * @brief Definitions for O_TMPFILE and mode requirement checking.
42+ *
43+ * O_TMPFILE allows creating unnamed temporary files, which requires a third
44+ * 'mode' argument just like O_CREAT. The NEEDS_MODE macro safely identifies
45+ * if the flags passed to open/openat require extracting this mode argument
46+ * from the variadic list to prevent creating files with 000 permissions.
47+ */
48+ #ifndef O_TMPFILE
49+ #define __O_TMPFILE 020000000
50+ #define O_TMPFILE (__O_TMPFILE | O_DIRECTORY)
51+ #endif
52+ #define NEEDS_MODE (flags ) (((flags) & O_CREAT) || (((flags) & O_TMPFILE) == O_TMPFILE))
53+
4054/**
4155 * @brief Defines the data type for ioctl request codes.
4256 *
@@ -134,6 +148,8 @@ static int g_sji_log_enabled = 0;
134148 */
135149static int (* real_open )(const char * pathname , int flags , ...) = NULL ;
136150static int (* real_open64 )(const char * pathname , int flags , ...) = NULL ;
151+ static int (* real_openat )(int dirfd , const char * pathname , int flags , ...) = NULL ;
152+ static int (* real_openat64 )(int dirfd , const char * pathname , int flags , ...) = NULL ;
137153static int (* real_ioctl )(int fd , ioctl_request_t request , ...) = NULL ;
138154static int (* real_epoll_ctl )(int epfd , int op , int fd , struct epoll_event * event ) = NULL ;
139155static int (* real_close )(int fd ) = NULL ;
@@ -414,6 +430,8 @@ __attribute__((constructor)) void init_interposer() {
414430 if (load_real_func ((void * )& real_stat , "stat" ) < 0 ) sji_log_error ("CRITICAL: Failed to load real 'stat'." );
415431 if (load_real_func ((void * )& real_lstat , "lstat" ) < 0 ) sji_log_error ("CRITICAL: Failed to load real 'lstat'." );
416432 load_real_func ((void * )& real_open64 , "open64" );
433+ load_real_func ((void * )& real_openat , "openat" );
434+ load_real_func ((void * )& real_openat64 , "openat64" );
417435 sji_log_info ("Selkies Joystick Interposer initialized. Logging is %s." , g_sji_log_enabled ? "ENABLED" : "DISABLED" );
418436}
419437
@@ -821,7 +839,6 @@ static int common_open_logic(const char *pathname, int flags, js_interposer_t **
821839 */
822840int open (const char * pathname , int flags , ...) {
823841 if (!real_open ) {
824- sji_log_error ("CRITICAL: real_open not loaded. Cannot proceed with open call." );
825842 errno = EFAULT ;
826843 return -1 ;
827844 }
@@ -830,11 +847,10 @@ int open(const char *pathname, int flags, ...) {
830847 int result_fd = common_open_logic (pathname , flags , & interposer );
831848
832849 if (result_fd == -2 ) {
833- mode_t mode = 0 ;
834- if (flags & O_CREAT ) {
850+ if (NEEDS_MODE (flags )) {
835851 va_list args ;
836852 va_start (args , flags );
837- mode = va_arg (args , mode_t );
853+ mode_t mode = va_arg (args , mode_t );
838854 va_end (args );
839855 result_fd = real_open (pathname , flags , mode );
840856 } else {
@@ -847,6 +863,7 @@ int open(const char *pathname, int flags, ...) {
847863#ifdef open64
848864#undef open64
849865#endif
866+
850867/**
851868 * @brief Intercepted `open64()` system call.
852869 *
@@ -864,7 +881,6 @@ int open(const char *pathname, int flags, ...) {
864881 */
865882int open64 (const char * pathname , int flags , ...) {
866883 if (!real_open64 && !real_open ) {
867- sji_log_error ("CRITICAL: Neither real_open64 nor real_open loaded. Cannot proceed with open64 call." );
868884 errno = EFAULT ;
869885 return -1 ;
870886 }
@@ -873,19 +889,139 @@ int open64(const char *pathname, int flags, ...) {
873889 int result_fd = common_open_logic (pathname , flags , & interposer );
874890
875891 if (result_fd == -2 ) {
876- mode_t mode = 0 ;
877- if (flags & O_CREAT ) {
892+ if (NEEDS_MODE (flags )) {
893+ va_list args ;
894+ va_start (args , flags );
895+ mode_t mode = va_arg (args , mode_t );
896+ va_end (args );
897+
898+ if (real_open64 ) {
899+ result_fd = real_open64 (pathname , flags , mode );
900+ } else {
901+ result_fd = real_open (pathname , flags , mode );
902+ }
903+ } else {
904+ if (real_open64 ) {
905+ result_fd = real_open64 (pathname , flags );
906+ } else {
907+ result_fd = real_open (pathname , flags );
908+ }
909+ }
910+ }
911+ return result_fd ;
912+ }
913+
914+ /**
915+ * @brief Intercepted `openat()` system call.
916+ *
917+ * Resolves the full path if a relative path and directory fd are provided.
918+ * Uses `common_open_logic()` to handle interposition for target device paths.
919+ * Safely extracts and passes the `mode` argument if file creation flags
920+ * (O_CREAT or O_TMPFILE) are present to prevent permission bugs.
921+ *
922+ * @param dirfd The directory file descriptor.
923+ * @param pathname The path to the file to open.
924+ * @param flags Flags for opening the file.
925+ * @param ... Optional `mode_t mode` argument if file creation flags are set.
926+ * @return A file descriptor on success, or -1 on error (`errno` is set).
927+ */
928+ int openat (int dirfd , const char * pathname , int flags , ...) {
929+ if (!real_openat ) {
930+ errno = EFAULT ;
931+ return -1 ;
932+ }
933+
934+ char full_path [4096 ];
935+ const char * check_path = pathname ;
936+
937+ if (pathname && pathname [0 ] != '/' && dirfd != AT_FDCWD ) {
938+ char procfd [64 ];
939+ snprintf (procfd , sizeof (procfd ), "/proc/self/fd/%d" , dirfd );
940+ ssize_t len = readlink (procfd , full_path , sizeof (full_path ) - 1 );
941+ if (len != -1 ) {
942+ full_path [len ] = '/' ;
943+ strncpy (full_path + len + 1 , pathname , sizeof (full_path ) - len - 2 );
944+ full_path [sizeof (full_path ) - 1 ] = '\0' ;
945+ check_path = full_path ;
946+ }
947+ }
948+
949+ js_interposer_t * interposer = NULL ;
950+ int result_fd = common_open_logic (check_path , flags , & interposer );
951+
952+ if (result_fd == -2 ) {
953+ if (NEEDS_MODE (flags )) {
878954 va_list args ;
879955 va_start (args , flags );
880- mode = va_arg (args , mode_t );
956+ mode_t mode = va_arg (args , mode_t );
881957 va_end (args );
958+ result_fd = real_openat (dirfd , pathname , flags , mode );
959+ } else {
960+ result_fd = real_openat (dirfd , pathname , flags );
961+ }
962+ }
963+ return result_fd ;
964+ }
965+
966+ #ifdef openat64
967+ #undef openat64
968+ #endif
969+
970+ /**
971+ * @brief Intercepted `openat64()` system call.
972+ *
973+ * 64-bit variant of the intercepted `openat()` system call. Resolves relative
974+ * paths, applies interposer logic, and safely handles variadic `mode` arguments.
975+ * Falls back to `real_openat()` if `real_openat64` is not available.
976+ *
977+ * @param dirfd The directory file descriptor.
978+ * @param pathname The path to the file to open.
979+ * @param flags Flags for opening the file.
980+ * @param ... Optional `mode_t mode` argument if file creation flags are set.
981+ * @return A file descriptor on success, or -1 on error (`errno` is set).
982+ */
983+ int openat64 (int dirfd , const char * pathname , int flags , ...) {
984+ if (!real_openat64 && !real_openat ) {
985+ errno = EFAULT ;
986+ return -1 ;
987+ }
988+
989+ char full_path [4096 ];
990+ const char * check_path = pathname ;
991+
992+ if (pathname && pathname [0 ] != '/' && dirfd != AT_FDCWD ) {
993+ char procfd [64 ];
994+ snprintf (procfd , sizeof (procfd ), "/proc/self/fd/%d" , dirfd );
995+ ssize_t len = readlink (procfd , full_path , sizeof (full_path ) - 1 );
996+ if (len != -1 ) {
997+ full_path [len ] = '/' ;
998+ strncpy (full_path + len + 1 , pathname , sizeof (full_path ) - len - 2 );
999+ full_path [sizeof (full_path ) - 1 ] = '\0' ;
1000+ check_path = full_path ;
8821001 }
1002+ }
1003+
1004+ js_interposer_t * interposer = NULL ;
1005+ int result_fd = common_open_logic (check_path , flags , & interposer );
8831006
884- if (real_open64 ) {
885- result_fd = (flags & O_CREAT ) ? real_open64 (pathname , flags , mode ) : real_open64 (pathname , flags );
1007+ if (result_fd == -2 ) {
1008+ if (NEEDS_MODE (flags )) {
1009+ va_list args ;
1010+ va_start (args , flags );
1011+ mode_t mode = va_arg (args , mode_t );
1012+ va_end (args );
1013+
1014+ if (real_openat64 ) {
1015+ result_fd = real_openat64 (dirfd , pathname , flags , mode );
1016+ } else {
1017+ result_fd = real_openat (dirfd , pathname , flags , mode );
1018+ }
8861019 } else {
887- sji_log_info ("real_open64 not available, falling back to real_open for: %s" , pathname );
888- result_fd = (flags & O_CREAT ) ? real_open (pathname , flags , mode ) : real_open (pathname , flags );
1020+ if (real_openat64 ) {
1021+ result_fd = real_openat64 (dirfd , pathname , flags );
1022+ } else {
1023+ result_fd = real_openat (dirfd , pathname , flags );
1024+ }
8891025 }
8901026 }
8911027 return result_fd ;
0 commit comments