2018-06-07 17:10:20 -07:00
/*
* Copyright © 2018 Alexey Dobriyan < adobriyan @ gmail . com >
*
* Permission to use , copy , modify , and distribute this software for any
* purpose with or without fee is hereby granted , provided that the above
* copyright notice and this permission notice appear in all copies .
*
* THE SOFTWARE IS PROVIDED " AS IS " AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS . IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL , DIRECT , INDIRECT , OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE , DATA OR PROFITS , WHETHER IN AN
* ACTION OF CONTRACT , NEGLIGENCE OR OTHER TORTIOUS ACTION , ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE .
*/
// Test that /proc/$KERNEL_THREAD/fd/ is empty.
2018-10-12 03:53:50 +08:00
2018-06-07 17:10:20 -07:00
# undef NDEBUG
# include <sys/syscall.h>
# include <assert.h>
# include <dirent.h>
# include <limits.h>
# include <stdio.h>
# include <string.h>
# include <sys/types.h>
# include <sys/stat.h>
# include <fcntl.h>
# include <unistd.h>
# include "proc.h"
# define PF_KHTREAD 0x00200000
/*
* Test for kernel threadness atomically with openat ( ) .
*
* Return / proc / $ PID / fd descriptor if process is kernel thread .
* Return - 1 if a process is userspace process .
*/
static int kernel_thread_fd ( unsigned int pid )
{
unsigned int flags = 0 ;
char buf [ 4096 ] ;
int dir_fd , fd ;
ssize_t rv ;
snprintf ( buf , sizeof ( buf ) , " /proc/%u " , pid ) ;
dir_fd = open ( buf , O_RDONLY | O_DIRECTORY ) ;
if ( dir_fd = = - 1 )
return - 1 ;
/*
* Believe it or not , struct task_struct : : flags is directly exposed
* to userspace !
*/
fd = openat ( dir_fd , " stat " , O_RDONLY ) ;
if ( fd = = - 1 ) {
close ( dir_fd ) ;
return - 1 ;
}
rv = read ( fd , buf , sizeof ( buf ) ) ;
close ( fd ) ;
if ( 0 < rv & & rv < = sizeof ( buf ) ) {
unsigned long long flags_ull ;
char * p , * end ;
int i ;
assert ( buf [ rv - 1 ] = = ' \n ' ) ;
buf [ rv - 1 ] = ' \0 ' ;
/* Search backwards: ->comm can contain whitespace and ')'. */
for ( i = 0 ; i < 43 ; i + + ) {
p = strrchr ( buf , ' ' ) ;
assert ( p ) ;
* p = ' \0 ' ;
}
p = strrchr ( buf , ' ' ) ;
assert ( p ) ;
flags_ull = xstrtoull ( p + 1 , & end ) ;
assert ( * end = = ' \0 ' ) ;
assert ( flags_ull = = ( unsigned int ) flags_ull ) ;
flags = flags_ull ;
}
fd = - 1 ;
if ( flags & PF_KHTREAD ) {
fd = openat ( dir_fd , " fd " , O_RDONLY | O_DIRECTORY ) ;
}
close ( dir_fd ) ;
return fd ;
}
static void test_readdir ( int fd )
{
DIR * d ;
struct dirent * de ;
d = fdopendir ( fd ) ;
assert ( d ) ;
de = xreaddir ( d ) ;
assert ( streq ( de - > d_name , " . " ) ) ;
assert ( de - > d_type = = DT_DIR ) ;
de = xreaddir ( d ) ;
assert ( streq ( de - > d_name , " .. " ) ) ;
assert ( de - > d_type = = DT_DIR ) ;
de = xreaddir ( d ) ;
assert ( ! de ) ;
}
static inline int sys_statx ( int dirfd , const char * pathname , int flags ,
unsigned int mask , void * stx )
{
return syscall ( SYS_statx , dirfd , pathname , flags , mask , stx ) ;
}
static void test_lookup_fail ( int fd , const char * pathname )
{
char stx [ 256 ] __attribute__ ( ( aligned ( 8 ) ) ) ;
int rv ;
rv = sys_statx ( fd , pathname , AT_SYMLINK_NOFOLLOW , 0 , ( void * ) stx ) ;
assert ( rv = = - 1 & & errno = = ENOENT ) ;
}
static void test_lookup ( int fd )
{
char buf [ 64 ] ;
unsigned int u ;
int i ;
for ( i = INT_MIN ; i < INT_MIN + 1024 ; i + + ) {
snprintf ( buf , sizeof ( buf ) , " %d " , i ) ;
test_lookup_fail ( fd , buf ) ;
}
for ( i = - 1024 ; i < 1024 ; i + + ) {
snprintf ( buf , sizeof ( buf ) , " %d " , i ) ;
test_lookup_fail ( fd , buf ) ;
}
for ( u = INT_MAX - 1024 ; u < ( unsigned int ) INT_MAX + 1024 ; u + + ) {
snprintf ( buf , sizeof ( buf ) , " %u " , u ) ;
test_lookup_fail ( fd , buf ) ;
}
for ( u = UINT_MAX - 1024 ; u ! = 0 ; u + + ) {
snprintf ( buf , sizeof ( buf ) , " %u " , u ) ;
test_lookup_fail ( fd , buf ) ;
}
}
int main ( void )
{
unsigned int pid ;
int fd ;
/*
* In theory this will loop indefinitely if kernel threads are exiled
* from / proc .
*
* Start with kthreadd .
*/
pid = 2 ;
while ( ( fd = kernel_thread_fd ( pid ) ) = = - 1 & & pid < 1024 ) {
pid + + ;
}
/* EACCES if run as non-root. */
if ( pid > = 1024 )
return 1 ;
test_readdir ( fd ) ;
test_lookup ( fd ) ;
return 0 ;
}