libc/fdcheck: add fdcheck module

In embedded development environments, due to the lack of address isolation between processes,
fd may be passed between processes and lead to misuse,

We have designed an fd cross-process automatic detection tool,
fdcheck_protect returns the fd containing the pid information,
indicating that the ownership of the current fd belongs to the pid and is not allowed to be used by other processes.
fdcheck_restore will obtain the true fd and check if the ownership of the fd is legal

For ease of understanding, let's give an example where
the following information is represented in 32-bit binary format

fd        00000000 00000000 00000000 10001010
pid       00000000 00000000 00000011 01010101
ret       00000000 00000011 01010101 10001010

Signed-off-by: hujun5 <hujun5@xiaomi.com>
This commit is contained in:
hujun5 2023-06-05 15:43:05 +08:00 committed by Xiang Xiao
parent d36e2a8394
commit 8fe8417ffb
7 changed files with 314 additions and 3 deletions

View file

@ -41,6 +41,10 @@
# include <android/fdsan.h>
#endif
#ifdef CONFIG_FDCHECK
# include <nuttx/fdcheck.h>
#endif
#include "inode/inode.h"
/****************************************************************************
@ -258,7 +262,12 @@ int file_allocate_from_tcb(FAR struct tcb_s *tcb, FAR struct inode *inode,
inode_addref(inode);
}
#ifdef CONFIG_FDCHECK
return
fdcheck_protect(i * CONFIG_NFILE_DESCRIPTORS_PER_BLOCK + j);
#else
return i * CONFIG_NFILE_DESCRIPTORS_PER_BLOCK + j;
#endif
}
}
while (++j < CONFIG_NFILE_DESCRIPTORS_PER_BLOCK);
@ -287,7 +296,11 @@ int file_allocate_from_tcb(FAR struct tcb_s *tcb, FAR struct inode *inode,
inode_addref(inode);
}
#ifdef CONFIG_FDCHECK
return fdcheck_protect(i * CONFIG_NFILE_DESCRIPTORS_PER_BLOCK);
#else
return i * CONFIG_NFILE_DESCRIPTORS_PER_BLOCK;
#endif
}
/****************************************************************************
@ -407,6 +420,10 @@ int fs_getfilep(int fd, FAR struct file **filep)
FAR struct filelist *list;
int ret;
#ifdef CONFIG_FDCHECK
fd = fdcheck_restore(fd);
#endif
DEBUGASSERT(filep != NULL);
*filep = NULL;
@ -487,6 +504,11 @@ int nx_dup2_from_tcb(FAR struct tcb_s *tcb, int fd1, int fd2)
return fd1;
}
#ifdef CONFIG_FDCHECK
fd1 = fdcheck_restore(fd1);
fd2 = fdcheck_restore(fd2);
#endif
list = nxsched_get_files_from_tcb(tcb);
/* Get the file descriptor list. It should not be NULL in this context. */
@ -533,7 +555,12 @@ int nx_dup2_from_tcb(FAR struct tcb_s *tcb, int fd1, int fd2)
nxmutex_unlock(&list->fl_lock);
file_close(&file);
#ifdef CONFIG_FDCHECK
return ret < 0 ? ret : fdcheck_protect(fd2);
#else
return ret < 0 ? ret : fd2;
#endif
}
/****************************************************************************
@ -612,6 +639,10 @@ int nx_close_from_tcb(FAR struct tcb_s *tcb, int fd)
FAR struct filelist *list;
int ret;
#ifdef CONFIG_FDCHECK
fd = fdcheck_restore(fd);
#endif
list = nxsched_get_files_from_tcb(tcb);
/* Perform the protected close operation */

View file

@ -39,6 +39,16 @@
#include "inode/inode.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#ifdef CONFIG_FDCHECK
# undef FD_ISSET
# define FD_ISSET(fd,set) \
(((((fd_set*)(set))->arr)[_FD_NDX(fd)] & (UINT32_C(1) << _FD_BIT(fd))) != 0)
#endif
/****************************************************************************
* Public Functions
****************************************************************************/
@ -91,6 +101,10 @@ int select(int nfds, FAR fd_set *readfds, FAR fd_set *writefds,
return ERROR;
}
#ifdef CONFIG_FDCHECK
nfds = fdcheck_restore(nfds - 1) + 1;
#endif
/* How many pollfd structures do we need to allocate? */
/* Initialize the descriptor list for poll() */
@ -138,7 +152,11 @@ int select(int nfds, FAR fd_set *readfds, FAR fd_set *writefds,
if (readfds && FD_ISSET(fd, readfds))
{
#ifdef CONFIG_FDCHECK
pollset[ndx].fd = fdcheck_protect(fd);
#else
pollset[ndx].fd = fd;
#endif
pollset[ndx].events |= POLLIN;
incr = 1;
}
@ -149,7 +167,11 @@ int select(int nfds, FAR fd_set *readfds, FAR fd_set *writefds,
if (writefds && FD_ISSET(fd, writefds))
{
#ifdef CONFIG_FDCHECK
pollset[ndx].fd = fdcheck_protect(fd);
#else
pollset[ndx].fd = fd;
#endif
pollset[ndx].events |= POLLOUT;
incr = 1;
}
@ -160,7 +182,11 @@ int select(int nfds, FAR fd_set *readfds, FAR fd_set *writefds,
if (exceptfds && FD_ISSET(fd, exceptfds))
{
#ifdef CONFIG_FDCHECK
pollset[ndx].fd = fdcheck_protect(fd);
#else
pollset[ndx].fd = fd;
#endif
incr = 1;
}

102
include/nuttx/fdcheck.h Normal file
View file

@ -0,0 +1,102 @@
/****************************************************************************
* include/nuttx/fdcheck.h
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you 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.
*
****************************************************************************/
#ifndef __INCLUDE_NUTTX_FDCHECK_H
#define __INCLUDE_NUTTX_FDCHECK_H
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
/****************************************************************************
* Public Function Prototypes
****************************************************************************/
#ifdef __cplusplus
#define EXTERN extern "C"
extern "C"
{
#else
#define EXTERN extern
#endif
#ifdef CONFIG_FDCHECK
/****************************************************************************
* Name: fdcheck_restore
*
* Description: Obtain original fd information
*
* Val carries the pid and fd information.
* The original fd information is stored in low bit of val.
* The pid information is stored in the high bit of val.
* For ease of understanding, let's give an example where
* the following information is represented in 32-bit binary format
*
* val 00000000 00000000 01010101 10001010
* fd 00000000 00000000 00000000 10001010
* pid 00000000 00000000 00000000 01010101
*
* In this function, we also check if the pid information is correct.
* If there is an error, it will panic.
*
* Input Parameters:
* val - this val carrying pid and original fd information
*
* Returned Value: none
*
****************************************************************************/
int fdcheck_restore(int fd);
/****************************************************************************
* Name: fdcheck_protect
*
* Description: Obtain the combined value of fd and pid
*
* the return value carries the pid and fd information.
* The original fd information is stored in low bit of val.
* The pid information is stored in high bit of val.
* For ease of understanding, let's give an example where
* the following information is represented in 32-bit binary format
*
* fd 00000000 00000000 00000000 10001010
* pid 00000000 00000000 00000000 01010101
* val 00000000 00000000 01010101 10001010
*
* Input Parameters:
* fd - original fd
*
* Returned Value: the combined value of fd and pid
*
****************************************************************************/
int fdcheck_protect(int fd);
#undef EXTERN
#ifdef __cplusplus
}
#endif
#endif
#endif /* __INCLUDE_NUTTX_FDCHECK_H */

View file

@ -32,6 +32,10 @@
#include <signal.h>
#include <sys/time.h>
#ifdef CONFIG_FDCHECK
# include <nuttx/fdcheck.h>
#endif
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
@ -71,12 +75,21 @@
/* Standard helper macros */
#define FD_CLR(fd,set) \
#ifdef CONFIG_FDCHECK
# define FD_CLR(fd,set) \
((((fd_set*)(set))->arr)[_FD_NDX(fdcheck_restore(fd))] &= ~(UINT32_C(1)<< _FD_BIT(fdcheck_restore(fd))))
# define FD_SET(fd,set) \
((((fd_set*)(set))->arr)[_FD_NDX(fdcheck_restore(fd))] |= (UINT32_C(1) << _FD_BIT(fdcheck_restore(fd))))
# define FD_ISSET(fd,set) \
(((((fd_set*)(set))->arr)[_FD_NDX(fdcheck_restore(fd))] & (UINT32_C(1) << _FD_BIT(fdcheck_restore(fd)))) != 0)
#else
# define FD_CLR(fd,set) \
((((fd_set*)(set))->arr)[_FD_NDX(fd)] &= ~(UINT32_C(1)<< _FD_BIT(fd)))
#define FD_SET(fd,set) \
# define FD_SET(fd,set) \
((((fd_set*)(set))->arr)[_FD_NDX(fd)] |= (UINT32_C(1) << _FD_BIT(fd)))
#define FD_ISSET(fd,set) \
# define FD_ISSET(fd,set) \
(((((fd_set*)(set))->arr)[_FD_NDX(fd)] & (UINT32_C(1) << _FD_BIT(fd))) != 0)
#endif
#define FD_ZERO(set) \
memset((set), 0, sizeof(fd_set))

View file

@ -68,6 +68,12 @@ config FDSAN
---help---
Enable the fdsan support
config FDCHECK
bool "Enable fdcheck"
default n
---help---
Enable the fdcheck support
config LIBC_FTOK_VFS_PATH
string "Relative path to ftok storage"
default "/var/ftok"

View file

@ -66,6 +66,12 @@ ifeq ($(CONFIG_FDSAN),y)
CSRCS += lib_fdsan.c
endif
# Fdcheck support
ifeq ($(CONFIG_FDCHECK),y)
CSRCS += lib_fdcheck.c
endif
# To ensure uname information is newest,
# add lib_utsname.o to phony target for force rebuild

View file

@ -0,0 +1,127 @@
/****************************************************************************
* libs/libc/misc/lib_fdcheck.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you 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.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/fdcheck.h>
#include <nuttx/lib/math32.h>
#include <debug.h>
#include <stdio.h>
#ifdef CONFIG_FDCHECK
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define FD_SHIFT 0
#define FD_BITS LOG2_CEIL(OPEN_MAX)
#define FD_MASK ((1 << FD_BITS) - 1)
#define PID_SHIFT (FD_BITS + FD_SHIFT)
#define PID_BITS (8 * sizeof(int) - 1 - PID_SHIFT)
#define PID_MASK ((1 << PID_BITS) - 1)
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: fdcheck_restore
*
* Description: Obtain original fd information
*
* Val carries the pid and fd information.
* The original fd information is stored in low bit of val.
* The pid information is stored in the high bit of val.
* For ease of understanding, let's give an example where
* the following information is represented in 32-bit binary format
*
* val 00000000 00000000 01010101 10001010
* fd 00000000 00000000 00000000 10001010
* pid 00000000 00000000 00000000 01010101
*
* In this function, we also check if the pid information is correct.
* If there is an error, it will panic.
*
* Input Parameters:
* val - this val carrying pid and original fd information
*
* Returned Value: none
*
****************************************************************************/
int fdcheck_restore(int val)
{
int pid_expect;
int pid_now;
if (val <= 2)
{
return val;
}
pid_expect = (val >> PID_SHIFT);
pid_now = (getpid() & PID_MASK);
if (pid_expect != pid_now)
{
ferr("pid_expect %d pid_now %d\n", pid_expect, pid_now);
PANIC();
}
return val & FD_MASK;
}
/****************************************************************************
* Name: fdcheck_protect
*
* Description: Obtain the combined value of fd and pid
*
* the return value carries the pid and fd information.
* The original fd information is stored in low bit of val.
* The pid information is stored in high bit of val.
* For ease of understanding, let's give an example where
* the following information is represented in 32-bit binary format
*
* fd 00000000 00000000 00000000 10001010
* pid 00000000 00000000 00000000 01010101
* val 00000000 00000000 01010101 10001010
*
* Input Parameters:
* fd - original fd
*
* Returned Value: the combined value of fd and pid
*
****************************************************************************/
int fdcheck_protect(int fd)
{
if (fd <= 2)
{
return fd;
}
return (fd & FD_MASK) | ((getpid() & PID_MASK) << PID_SHIFT);
}
#endif