opnsense-src/tests/sys/fs/fusefs/open.cc
Alan Somers 8e7657374a fusefs: coverity cleanup in the tests
Address the following defects reported by Coverity:

* Structurally dead code (CID 1404366): set m_quit before FAIL, not after

* Unchecked return value of sysctlbyname (CID 1404321)

* Unchecked return value of stat(2) (CID 1404471)

* Unchecked return value of open(2) (CID 1404402, 1404529)

* Unchecked return value of dup(2) (CID 1404478)

* Buffer overflows. These are all false positives caused by the fact that
  Coverity thinks I'm using a buffer to store strings, when in fact I'm
  really just using it to store a byte array that happens to be initialized
  with a string. I'm changing the type from char to uint8_t in the hopes
  that it will placate Coverity. (CID 1404338, 1404350, 1404367, 1404376,
  1404379, 1404381, 1404388, 1404403, 1404425, 1404433, 1404434, 1404474,
  1404480, 1404484, 1404503, 1404505)

* False positive file descriptor leak. I'm going to try to fix this with
  Coverity modeling, but I'll also change an EXPECT to ASSERT so we don't
  perform meaningless assertions after the failure. (CID 1404320, 1404324,
  1404440, 1404445).

* Unannotated file descriptor leak. This will be followed up by a Coverity
  modeling change. (CID 1404326, 1404334, 1404336, 1404357, 1404361,
  1404372, 1404391, 1404395, 1404409, 1404430, 1404448, 1404451, 1404455,
  1404457, 1404458, 1404460)

* Uninitialized variables in C++ constructors (CID 1404327, 1404346). In the
  case of m_maxphys, this actually led to part of the FUSE_INIT's response
  being set to stack garbage during the WriteCluster::clustering test.

* Uninitialized sun_len field in struct sockaddr_un (CID 1404330, 1404371,
  1404429).

Reported by:	Coverity
Reviewed by:	emaste
MFC after:	3 days
Sponsored by:	The FreeBSD Foundation
Differential Revision:	https://reviews.freebsd.org/D21457
2019-09-06 19:50:45 +00:00

264 lines
6.9 KiB
C++

/*-
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
* Copyright (c) 2019 The FreeBSD Foundation
*
* This software was developed by BFF Storage Systems, LLC under sponsorship
* from the FreeBSD Foundation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. 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 AUTHOR 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 AUTHOR 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.
*
* $FreeBSD$
*/
extern "C" {
#include <sys/wait.h>
#include <fcntl.h>
}
#include "mockfs.hh"
#include "utils.hh"
using namespace testing;
class Open: public FuseTest {
public:
/* Test an OK open of a file with the given flags */
void test_ok(int os_flags, int fuse_flags) {
const char FULLPATH[] = "mountpoint/some_file.txt";
const char RELPATH[] = "some_file.txt";
uint64_t ino = 42;
int fd;
FuseTest::expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1);
EXPECT_CALL(*m_mock, process(
ResultOf([=](auto in) {
return (in.header.opcode == FUSE_OPEN &&
in.body.open.flags == (uint32_t)fuse_flags &&
in.header.nodeid == ino);
}, Eq(true)),
_)
).WillOnce(Invoke(ReturnImmediate([](auto in __unused, auto& out) {
out.header.len = sizeof(out.header);
SET_OUT_HEADER_LEN(out, open);
})));
fd = open(FULLPATH, os_flags);
EXPECT_LE(0, fd) << strerror(errno);
leak(fd);
}
};
/*
* fusefs(5) does not support I/O on device nodes (neither does UFS). But it
* shouldn't crash
*/
TEST_F(Open, chr)
{
const char FULLPATH[] = "mountpoint/zero";
const char RELPATH[] = "zero";
uint64_t ino = 42;
EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
.WillRepeatedly(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
SET_OUT_HEADER_LEN(out, entry);
out.body.entry.attr.mode = S_IFCHR | 0644;
out.body.entry.nodeid = ino;
out.body.entry.attr.nlink = 1;
out.body.entry.attr_valid = UINT64_MAX;
out.body.entry.attr.rdev = 44; /* /dev/zero's rdev */
})));
ASSERT_EQ(-1, open(FULLPATH, O_RDONLY));
EXPECT_EQ(EOPNOTSUPP, errno);
}
/*
* The fuse daemon fails the request with enoent. This usually indicates a
* race condition: some other FUSE client removed the file in between when the
* kernel checked for it with lookup and tried to open it
*/
TEST_F(Open, enoent)
{
const char FULLPATH[] = "mountpoint/some_file.txt";
const char RELPATH[] = "some_file.txt";
uint64_t ino = 42;
expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1);
EXPECT_CALL(*m_mock, process(
ResultOf([=](auto in) {
return (in.header.opcode == FUSE_OPEN &&
in.header.nodeid == ino);
}, Eq(true)),
_)
).WillOnce(Invoke(ReturnErrno(ENOENT)));
ASSERT_EQ(-1, open(FULLPATH, O_RDONLY));
EXPECT_EQ(ENOENT, errno);
}
/*
* The daemon is responsible for checking file permissions (unless the
* default_permissions mount option was used)
*/
TEST_F(Open, eperm)
{
const char FULLPATH[] = "mountpoint/some_file.txt";
const char RELPATH[] = "some_file.txt";
uint64_t ino = 42;
expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1);
EXPECT_CALL(*m_mock, process(
ResultOf([=](auto in) {
return (in.header.opcode == FUSE_OPEN &&
in.header.nodeid == ino);
}, Eq(true)),
_)
).WillOnce(Invoke(ReturnErrno(EPERM)));
ASSERT_EQ(-1, open(FULLPATH, O_RDONLY));
EXPECT_EQ(EPERM, errno);
}
/*
* fusefs must issue multiple FUSE_OPEN operations if clients with different
* credentials open the same file, even if they use the same mode. This is
* necessary so that the daemon can validate each set of credentials.
*/
TEST_F(Open, multiple_creds)
{
const static char FULLPATH[] = "mountpoint/some_file.txt";
const static char RELPATH[] = "some_file.txt";
int fd1, status;
const static uint64_t ino = 42;
const static uint64_t fh0 = 100, fh1 = 200;
/* Fork a child to open the file with different credentials */
fork(false, &status, [&] {
expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 2);
EXPECT_CALL(*m_mock, process(
ResultOf([=](auto in) {
return (in.header.opcode == FUSE_OPEN &&
in.header.pid == (uint32_t)getpid() &&
in.header.nodeid == ino);
}, Eq(true)),
_)
).WillOnce(Invoke(
ReturnImmediate([](auto in __unused, auto& out) {
out.body.open.fh = fh0;
out.header.len = sizeof(out.header);
SET_OUT_HEADER_LEN(out, open);
})));
EXPECT_CALL(*m_mock, process(
ResultOf([=](auto in) {
return (in.header.opcode == FUSE_OPEN &&
in.header.pid != (uint32_t)getpid() &&
in.header.nodeid == ino);
}, Eq(true)),
_)
).WillOnce(Invoke(
ReturnImmediate([](auto in __unused, auto& out) {
out.body.open.fh = fh1;
out.header.len = sizeof(out.header);
SET_OUT_HEADER_LEN(out, open);
})));
expect_flush(ino, 2, ReturnErrno(0));
expect_release(ino, fh0);
expect_release(ino, fh1);
fd1 = open(FULLPATH, O_RDONLY);
EXPECT_LE(0, fd1) << strerror(errno);
}, [] {
int fd0;
fd0 = open(FULLPATH, O_RDONLY);
if (fd0 < 0) {
perror("open");
return(1);
}
return 0;
}
);
ASSERT_EQ(0, WEXITSTATUS(status));
close(fd1);
}
/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236340 */
TEST_F(Open, DISABLED_o_append)
{
test_ok(O_WRONLY | O_APPEND, O_WRONLY | O_APPEND);
}
/* The kernel is supposed to filter out this flag */
TEST_F(Open, o_creat)
{
test_ok(O_WRONLY | O_CREAT, O_WRONLY);
}
/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236340 */
TEST_F(Open, DISABLED_o_direct)
{
test_ok(O_WRONLY | O_DIRECT, O_WRONLY | O_DIRECT);
}
/* The kernel is supposed to filter out this flag */
TEST_F(Open, o_excl)
{
test_ok(O_WRONLY | O_EXCL, O_WRONLY);
}
TEST_F(Open, o_exec)
{
test_ok(O_EXEC, O_EXEC);
}
/* The kernel is supposed to filter out this flag */
TEST_F(Open, o_noctty)
{
test_ok(O_WRONLY | O_NOCTTY, O_WRONLY);
}
TEST_F(Open, o_rdonly)
{
test_ok(O_RDONLY, O_RDONLY);
}
/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236340 */
TEST_F(Open, DISABLED_o_trunc)
{
test_ok(O_WRONLY | O_TRUNC, O_WRONLY | O_TRUNC);
}
TEST_F(Open, o_wronly)
{
test_ok(O_WRONLY, O_WRONLY);
}
TEST_F(Open, o_rdwr)
{
test_ok(O_RDWR, O_RDWR);
}