nuttx/libs/libc/stdio/lib_fopen.c
hujun5 90387a5b41 libc/misc: add fdsan module
FD (file descriptor) is widely used in system software development,
and almost all implementations of posix os (including nuttx) use FD as an index.
the value of fd needs to be allocated starting from the minimum available value of 3, and each process has a copy,
so the same fd value is very easy to reuse in the program.

In multi threaded or multi process environments without address isolation,
If the ownership, global variables, and competition relationships of fd are not properly handled,
there may be issues with fd duplication or accidental closure.
Further leading to the following issues, which are difficult to troubleshoot.

1. Security vulnerability: the fd we wrote is not the expected fd and will be accessed by hackers to obtain data
2. Program exceptions or crashes: write or read fd failures, and program logic errors
3. The structured file XML or database is damaged: the data format written to the database is not the expected format.

The implementation principle of fdsan is based on the implementation of Android
https://android.googlesource.com/platform/bionic/+/master/docs/fdsan.md

Signed-off-by: hujun5 <hujun5@xiaomi.com>
2023-05-17 10:24:42 +08:00

339 lines
8.5 KiB
C

/****************************************************************************
* libs/libc/stdio/lib_fopen.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/config.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#ifdef CONFIG_FDSAN
# include <android/fdsan.h>
#endif
#include "libc.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/* Open mode flags */
#define MODE_R (1 << 0) /* Bit 0: "r{b|x|+}" open for reading */
#define MODE_W (1 << 1) /* Bit 1: "w{b|x|+}" open for writing, truncating,
* or creating file */
#define MODE_A (1 << 2) /* Bit 2: "a{b|x|+}" open for writing, appending
* the to file */
#define MODE_NONE 0 /* No access mode determined */
#define MODE_MASK (MODE_R | MODE_W | MODE_A)
#define FLAG_KEEP (O_TEXT | O_CLOEXEC | O_EXCL)
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: fdopen
****************************************************************************/
FAR FILE *fdopen(int fd, FAR const char *mode)
{
FAR FILE *filep = NULL;
int oflags;
int ret;
/* Map the open mode string to open flags */
oflags = lib_mode2oflags(mode);
if (oflags >= 0)
{
ret = fs_fdopen(fd, oflags, NULL, &filep);
if (ret < 0)
{
set_errno(-ret);
}
}
return filep;
}
/****************************************************************************
* Name: fopen
****************************************************************************/
FAR FILE *fopen(FAR const char *path, FAR const char *mode)
{
FAR FILE *filep = NULL;
int oflags;
int fd;
int ret;
/* Map the open mode string to open flags */
oflags = lib_mode2oflags(mode);
if (oflags < 0)
{
return NULL;
}
/* Open the file */
fd = open(path, oflags, 0666);
/* If the open was successful, then call fdopen() using the file
* descriptor returned by open. If open failed, then just return the
* NULL stream -- open() has already set the errno.
*/
if (fd >= 0)
{
ret = fs_fdopen(fd, oflags, NULL, &filep);
if (ret < 0)
{
/* Don't forget to close the file descriptor if any other
* failures are reported by fdopen().
*/
close(fd);
set_errno(-ret);
filep = NULL;
}
}
#ifdef CONFIG_FDSAN
android_fdsan_exchange_owner_tag(fd, 0,
android_fdsan_create_owner_tag(ANDROID_FDSAN_OWNER_TYPE_FILE,
(uintptr_t)filep));
#endif
return filep;
}
/****************************************************************************
* Name: lib_mode2oflags
****************************************************************************/
int lib_mode2oflags(FAR const char *mode)
{
unsigned int state;
int oflags;
/* Verify that a mode string was provided. */
DEBUGASSERT(mode);
/* Parse the mode string to determine the corresponding open flags */
state = MODE_NONE;
oflags = 0;
for (; *mode; mode++)
{
switch (*mode)
{
/* Open for read access ("r{b|x|+}") */
case 'r' :
if (state == MODE_NONE)
{
/* Open for read access */
oflags = O_RDOK | O_TEXT;
state = MODE_R;
}
else
{
goto errout;
}
break;
/* Open for write access ("w{b|x|+}") */
case 'w' :
if (state == MODE_NONE)
{
/* Open for write access, truncating any existing file */
oflags = O_WROK | O_CREAT | O_TRUNC | O_TEXT;
state = MODE_W;
}
else
{
goto errout;
}
break;
/* Open for write/append access ("a{b|x|+}") */
case 'a' :
if (state == MODE_NONE)
{
/* Write to the end of the file */
oflags = O_WROK | O_CREAT | O_APPEND | O_TEXT;
state = MODE_A;
}
else
{
goto errout;
}
break;
/* Open for update access ("{r|w|a|b|x}+") */
case '+' :
switch (state & MODE_MASK)
{
case MODE_R:
{
/* Retain any binary and exclusive mode selections */
oflags &= FLAG_KEEP;
/* Open for read/write access */
oflags |= O_RDWR;
}
break;
case MODE_W:
{
/* Retain any binary and exclusive mode selections */
oflags &= FLAG_KEEP;
/* Open for write read/access, truncating any existing
* file.
*/
oflags |= O_RDWR | O_CREAT | O_TRUNC;
}
break;
case MODE_A:
{
/* Retain any binary and exclusive mode selections */
oflags &= FLAG_KEEP;
/* Read from the beginning of the file; write to the
* end,
*/
oflags |= O_RDWR | O_CREAT | O_APPEND;
}
break;
default:
goto errout;
break;
}
break;
/* Open for binary access ("{r|w|a|x|+}b") */
case 'b' :
if ((state & MODE_MASK) != MODE_NONE)
{
/* The file is opened in binary mode */
oflags &= ~O_TEXT;
}
else
{
goto errout;
}
break;
/* Open for close on execute */
case 'e' :
if ((state & MODE_MASK) != MODE_NONE)
{
/* The file will be closed on execute */
oflags |= O_CLOEXEC;
}
else
{
goto errout;
}
break;
/* Open for exclusive access ("{r|w|a|b|+}x") */
case 'x' :
if ((state & MODE_MASK) != MODE_NONE)
{
/* The file is opened in exclusive mode */
oflags |= O_EXCL;
}
else
{
goto errout;
}
break;
/* Open for text (translated) access ("{r|w|a|x|+}t") */
case 't' :
if ((state & MODE_MASK) != MODE_NONE)
{
/* The file is opened in text mode */
oflags |= O_TEXT;
}
else
{
goto errout;
}
break;
/* Unrecognized or unsupported mode */
default:
goto errout;
break;
}
}
return oflags;
/* Both fopen and fdopen should fail with errno == EINVAL if the mode
* string is invalid.
*/
errout:
set_errno(EINVAL);
return ERROR;
}