nuttx/libs/libc/misc/lib_fdsan.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

207 lines
6.8 KiB
C

/****************************************************************************
* libs/libc/misc/lib_fdsan.c
* Copyright (C) 2018 The Android Open Source Project
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <android/fdsan.h>
#include <debug.h>
#include <pthread.h>
#include <stdio.h>
#include <setjmp.h>
#include <sys/ioctl.h>
/****************************************************************************
* Private Functions
****************************************************************************/
uint64_t android_fdsan_get_tag_value(uint64_t tag)
{
/* Lop off the most significant byte and sign extend. */
return (uint64_t)((int64_t)(tag << 8) >> 8);
}
const char *android_fdsan_get_tag_type(uint64_t tag)
{
uint64_t high_bits;
uint64_t type = tag >> 56;
switch (type)
{
case ANDROID_FDSAN_OWNER_TYPE_FILE:
return "FILE*";
case ANDROID_FDSAN_OWNER_TYPE_DIR:
return "DIR*";
case ANDROID_FDSAN_OWNER_TYPE_UNIQUE_FD:
return "unique_fd";
case ANDROID_FDSAN_OWNER_TYPE_FILEINPUTSTREAM:
return "FileInputStream";
case ANDROID_FDSAN_OWNER_TYPE_FILEOUTPUTSTREAM:
return "FileOutputStream";
case ANDROID_FDSAN_OWNER_TYPE_RANDOMACCESSFILE:
return "RandomAccessFile";
case ANDROID_FDSAN_OWNER_TYPE_PARCELFILEDESCRIPTOR:
return "ParcelFileDescriptor";
case ANDROID_FDSAN_OWNER_TYPE_SQLITE:
return "sqlite";
case ANDROID_FDSAN_OWNER_TYPE_ART_FDFILE:
return "ART FdFile";
case ANDROID_FDSAN_OWNER_TYPE_DATAGRAMSOCKETIMPL:
return "DatagramSocketImpl";
case ANDROID_FDSAN_OWNER_TYPE_SOCKETIMPL:
return "SocketImpl";
case ANDROID_FDSAN_OWNER_TYPE_ZIPARCHIVE:
return "ZipArchive";
case ANDROID_FDSAN_OWNER_TYPE_GENERIC_00:
default:
return "native object of unknown type";
case ANDROID_FDSAN_OWNER_TYPE_GENERIC_FF:
/********************************************************************
* If bits 48 to 56 are set,
* this is a sign-extended generic native pointer
********************************************************************/
high_bits = tag >> 48;
if (high_bits == (1 << 16) - 1)
{
return "native object of unknown type";
}
return "Java object of unknown type";
}
}
/****************************************************************************
* Public Functions
****************************************************************************/
uint64_t android_fdsan_create_owner_tag(android_fdsan_owner_type_t type,
uint64_t tag)
{
uint64_t result;
uint64_t mask;
if (tag == 0)
{
return 0;
}
DEBUGASSERT((type & 0xff) == type);
result = (uint64_t)(type) << 56;
mask = ((uint64_t)1 << 56) - 1;
result |= tag & mask;
return result;
}
int android_fdsan_close_with_tag(int fd, uint64_t expected_tag)
{
int ret;
android_fdsan_exchange_owner_tag(fd, expected_tag, 0);
ret = close(fd);
/**************************************************************************
* If we were expecting to close with a tag, abort on EBADF.
**************************************************************************/
if (expected_tag && ret == -1 && errno == EBADF)
{
ferr("double-close of file descriptor %d detected\n", fd);
PANIC();
}
return ret;
}
void android_fdsan_exchange_owner_tag(int fd, uint64_t expected_tag,
uint64_t new_tag)
{
uint64_t tag;
int ret;
ret = ioctl(fd, FIOC_GETTAG, &tag);
if (ret < 0)
{
return;
}
if (tag == expected_tag)
{
ret = ioctl(fd, FIOC_SETTAG, &new_tag);
DEBUGASSERT(ret == 0);
}
else
{
if (expected_tag && tag)
{
ferr("failed to exchange ownership of file descriptor: fd %d is "
"owned by %s 0x%" PRIx64 ", was expected"
"to be owned by %s 0x%" PRIx64 "\n",
fd, android_fdsan_get_tag_type(tag),
android_fdsan_get_tag_value(tag),
android_fdsan_get_tag_type(expected_tag),
android_fdsan_get_tag_value(expected_tag));
PANIC();
}
else if (expected_tag && !tag)
{
ferr("failed to exchange ownership of file descriptor: fd %d is "
"unowned, was expected to be owned by %s 0x%" PRIx64 "\n",
fd, android_fdsan_get_tag_type(expected_tag),
android_fdsan_get_tag_value(expected_tag));
PANIC();
}
else if (!expected_tag && tag)
{
ferr("failed to exchange ownership of file descriptor: fd %d is "
"owned by %s 0x%" PRIx64 ", was expected to be unowned\n",
fd, android_fdsan_get_tag_type(tag),
android_fdsan_get_tag_value(tag));
PANIC();
}
else if (!expected_tag && !tag)
{
/******************************************************************
* This should never happen: our CAS failed,
* but expected == actual?
******************************************************************/
ferr("fdsan atomic_compare_exchange_strong failed unexpectedly "
"while exchanging owner tag\n");
PANIC();
}
}
}