From 8fe8417ffb6bc5f4bd9461b7ff528f39c4073546 Mon Sep 17 00:00:00 2001 From: hujun5 Date: Mon, 5 Jun 2023 15:43:05 +0800 Subject: [PATCH] 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 --- fs/inode/fs_files.c | 31 +++++++++ fs/vfs/fs_select.c | 26 +++++++ include/nuttx/fdcheck.h | 102 ++++++++++++++++++++++++++++ include/sys/select.h | 19 +++++- libs/libc/misc/Kconfig | 6 ++ libs/libc/misc/Make.defs | 6 ++ libs/libc/misc/lib_fdcheck.c | 127 +++++++++++++++++++++++++++++++++++ 7 files changed, 314 insertions(+), 3 deletions(-) create mode 100644 include/nuttx/fdcheck.h create mode 100644 libs/libc/misc/lib_fdcheck.c diff --git a/fs/inode/fs_files.c b/fs/inode/fs_files.c index e75cd91019..4060611634 100644 --- a/fs/inode/fs_files.c +++ b/fs/inode/fs_files.c @@ -41,6 +41,10 @@ # include #endif +#ifdef CONFIG_FDCHECK +# include +#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 */ diff --git a/fs/vfs/fs_select.c b/fs/vfs/fs_select.c index cffbb06f06..ac628451ca 100644 --- a/fs/vfs/fs_select.c +++ b/fs/vfs/fs_select.c @@ -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; } diff --git a/include/nuttx/fdcheck.h b/include/nuttx/fdcheck.h new file mode 100644 index 0000000000..abf7e4bacc --- /dev/null +++ b/include/nuttx/fdcheck.h @@ -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 + +/**************************************************************************** + * 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 */ diff --git a/include/sys/select.h b/include/sys/select.h index 01997e9037..b2cd159665 100644 --- a/include/sys/select.h +++ b/include/sys/select.h @@ -32,6 +32,10 @@ #include #include +#ifdef CONFIG_FDCHECK +# include +#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)) diff --git a/libs/libc/misc/Kconfig b/libs/libc/misc/Kconfig index 9a7c9a24e8..c4825d5210 100644 --- a/libs/libc/misc/Kconfig +++ b/libs/libc/misc/Kconfig @@ -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" diff --git a/libs/libc/misc/Make.defs b/libs/libc/misc/Make.defs index edf17d3438..7fc335daaf 100644 --- a/libs/libc/misc/Make.defs +++ b/libs/libc/misc/Make.defs @@ -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 diff --git a/libs/libc/misc/lib_fdcheck.c b/libs/libc/misc/lib_fdcheck.c new file mode 100644 index 0000000000..b1587aae97 --- /dev/null +++ b/libs/libc/misc/lib_fdcheck.c @@ -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 +#include + +#include +#include + +#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