opnsense-src/tests/sys/fs/fusefs/dev_fuse_poll.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

232 lines
6.6 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$
*/
/*
* This file tests different polling methods for the /dev/fuse device
*/
extern "C" {
#include <fcntl.h>
#include <semaphore.h>
#include <unistd.h>
}
#include "mockfs.hh"
#include "utils.hh"
using namespace testing;
const char FULLPATH[] = "mountpoint/some_file.txt";
const char RELPATH[] = "some_file.txt";
const uint64_t ino = 42;
const mode_t access_mode = R_OK;
/*
* Translate a poll method's string representation to the enum value.
* Using strings with ::testing::Values gives better output with
* --gtest_list_tests
*/
enum poll_method poll_method_from_string(const char *s)
{
if (0 == strcmp("BLOCKING", s))
return BLOCKING;
else if (0 == strcmp("KQ", s))
return KQ;
else if (0 == strcmp("POLL", s))
return POLL;
else
return SELECT;
}
class DevFusePoll: public FuseTest, public WithParamInterface<const char *> {
virtual void SetUp() {
m_pm = poll_method_from_string(GetParam());
FuseTest::SetUp();
}
};
class Kqueue: public FuseTest {
virtual void SetUp() {
m_pm = KQ;
FuseTest::SetUp();
}
};
TEST_P(DevFusePoll, access)
{
expect_access(FUSE_ROOT_ID, X_OK, 0);
expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1);
expect_access(ino, access_mode, 0);
ASSERT_EQ(0, access(FULLPATH, access_mode)) << strerror(errno);
}
/* Ensure that we wake up pollers during unmount */
TEST_P(DevFusePoll, destroy)
{
expect_forget(FUSE_ROOT_ID, 1);
expect_destroy(0);
m_mock->unmount();
}
INSTANTIATE_TEST_CASE_P(PM, DevFusePoll,
::testing::Values("BLOCKING", "KQ", "POLL", "SELECT"));
static void* statter(void* arg) {
const char *name;
struct stat sb;
name = (const char*)arg;
return ((void*)(intptr_t)stat(name, &sb));
}
/*
* A kevent's data field should contain the number of operations available to
* be immediately read.
*/
TEST_F(Kqueue, data)
{
pthread_t th0, th1, th2;
sem_t sem0, sem1;
int nready0, nready1, nready2;
uint64_t foo_ino = 42;
uint64_t bar_ino = 43;
uint64_t baz_ino = 44;
Sequence seq;
void *th_ret;
ASSERT_EQ(0, sem_init(&sem0, 0, 0)) << strerror(errno);
ASSERT_EQ(0, sem_init(&sem1, 0, 0)) << strerror(errno);
EXPECT_LOOKUP(FUSE_ROOT_ID, "foo")
.WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
SET_OUT_HEADER_LEN(out, entry);
out.body.entry.entry_valid = UINT64_MAX;
out.body.entry.attr.mode = S_IFREG | 0644;
out.body.entry.nodeid = foo_ino;
})));
EXPECT_LOOKUP(FUSE_ROOT_ID, "bar")
.WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
SET_OUT_HEADER_LEN(out, entry);
out.body.entry.entry_valid = UINT64_MAX;
out.body.entry.attr.mode = S_IFREG | 0644;
out.body.entry.nodeid = bar_ino;
})));
EXPECT_LOOKUP(FUSE_ROOT_ID, "baz")
.WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
SET_OUT_HEADER_LEN(out, entry);
out.body.entry.entry_valid = UINT64_MAX;
out.body.entry.attr.mode = S_IFREG | 0644;
out.body.entry.nodeid = baz_ino;
})));
EXPECT_CALL(*m_mock, process(
ResultOf([=](auto in) {
return (in.header.opcode == FUSE_GETATTR &&
in.header.nodeid == foo_ino);
}, Eq(true)),
_)
)
.WillOnce(Invoke(ReturnImmediate([&](auto in, auto& out) {
nready0 = m_mock->m_nready;
sem_post(&sem0);
// Block the daemon so we can accumulate a few more ops
sem_wait(&sem1);
out.header.unique = in.header.unique;
out.header.error = -EIO;
out.header.len = sizeof(out.header);
})));
EXPECT_CALL(*m_mock, process(
ResultOf([=](auto in) {
return (in.header.opcode == FUSE_GETATTR &&
(in.header.nodeid == bar_ino ||
in.header.nodeid == baz_ino));
}, Eq(true)),
_)
).InSequence(seq)
.WillOnce(Invoke(ReturnImmediate([&](auto in, auto& out) {
nready1 = m_mock->m_nready;
out.header.unique = in.header.unique;
out.header.error = -EIO;
out.header.len = sizeof(out.header);
})));
EXPECT_CALL(*m_mock, process(
ResultOf([=](auto in) {
return (in.header.opcode == FUSE_GETATTR &&
(in.header.nodeid == bar_ino ||
in.header.nodeid == baz_ino));
}, Eq(true)),
_)
).InSequence(seq)
.WillOnce(Invoke(ReturnImmediate([&](auto in, auto& out) {
nready2 = m_mock->m_nready;
out.header.unique = in.header.unique;
out.header.error = -EIO;
out.header.len = sizeof(out.header);
})));
/*
* Create cached lookup entries for these files. It seems that only
* one thread at a time can be in VOP_LOOKUP for a given directory
*/
access("mountpoint/foo", F_OK);
access("mountpoint/bar", F_OK);
access("mountpoint/baz", F_OK);
ASSERT_EQ(0, pthread_create(&th0, NULL, statter,
__DECONST(void*, "mountpoint/foo"))) << strerror(errno);
EXPECT_EQ(0, sem_wait(&sem0)) << strerror(errno);
ASSERT_EQ(0, pthread_create(&th1, NULL, statter,
__DECONST(void*, "mountpoint/bar"))) << strerror(errno);
ASSERT_EQ(0, pthread_create(&th2, NULL, statter,
__DECONST(void*, "mountpoint/baz"))) << strerror(errno);
nap(); // Allow th1 and th2 to send their ops to the daemon
EXPECT_EQ(0, sem_post(&sem1)) << strerror(errno);
pthread_join(th0, &th_ret);
ASSERT_EQ(-1, (intptr_t)th_ret);
pthread_join(th1, &th_ret);
ASSERT_EQ(-1, (intptr_t)th_ret);
pthread_join(th2, &th_ret);
ASSERT_EQ(-1, (intptr_t)th_ret);
EXPECT_EQ(1, nready0);
EXPECT_EQ(2, nready1);
EXPECT_EQ(1, nready2);
sem_destroy(&sem0);
sem_destroy(&sem1);
}