// Copyright(c) 2019-2020, Intel Corporation
//
// 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.
// * Neither the name of Intel Corporation nor the names of its contributors
// may be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// 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.
#include "libbitstream/bits_utils.h"
extern "C" {
bool opae_bitstream_path_invalid_chars(const char *path,
size_t len);
bool opae_bitstream_path_not_file(const char *path);
bool opae_bitstream_path_contains_dotdot(const char *path,
size_t len);
bool opae_bitstream_path_contains_symlink(const char *path,
size_t len);
}
#include <config.h>
#include <opae/fpga.h>
#include "gtest/gtest.h"
#include "mock/test_system.h"
using namespace opae::testing;
class bits_utils_c_p : public ::testing::TestWithParam<std::string> {
protected:
virtual void SetUp() override {
std::string platform_key = GetParam();
ASSERT_TRUE(test_platform::exists(platform_key));
platform_ = test_platform::get(platform_key);
system_ = test_system::instance();
system_->initialize();
system_->prepare_syfs(platform_);
j_root_ = nullptr;
}
virtual void TearDown() override {
if (j_root_)
json_object_put(j_root_);
system_->finalize();
}
json_object *parse(const char *json_str)
{
enum json_tokener_error j_err = json_tokener_success;
return j_root_ = json_tokener_parse_verbose(json_str, &j_err);
}
json_object *j_root_;
test_platform platform_;
test_system *system_;
};
/**
* @test string_err0
* @brief Test: opae_bitstream_get_json_string
* @details If the given name doesn't exist,<br>
* the fn returns FPGA_EXCEPTION.<br>
*/
TEST_P(bits_utils_c_p, string_err0) {
const char *mdata =
R"mdata({
"a": "foo"
})mdata";
json_object *root;
char *value = nullptr;
root = parse(mdata);
ASSERT_NE(root, nullptr);
EXPECT_EQ(opae_bitstream_get_json_string(root,
"b",
&value),
FPGA_EXCEPTION);
EXPECT_EQ(value, nullptr);
}
/**
* @test string_err1
* @brief Test: opae_bitstream_get_json_string
* @details If the given name exists,<br>
* but isn't a string,<br>
* the fn returns FPGA_EXCEPTION.<br>
*/
TEST_P(bits_utils_c_p, string_err1) {
const char *mdata =
R"mdata({
"a": 3
})mdata";
json_object *root;
char *value = nullptr;
root = parse(mdata);
ASSERT_NE(root, nullptr);
EXPECT_EQ(opae_bitstream_get_json_string(root,
"a",
&value),
FPGA_EXCEPTION);
EXPECT_EQ(value, nullptr);
}
/**
* @test int_err0
* @brief Test: opae_bitstream_get_json_int
* @details If the given name doesn't exist,<br>
* the fn returns FPGA_EXCEPTION.<br>
*/
TEST_P(bits_utils_c_p, int_err0) {
const char *mdata =
R"mdata({
"a": 3
})mdata";
json_object *root;
int value = 0;
root = parse(mdata);
ASSERT_NE(root, nullptr);
EXPECT_EQ(opae_bitstream_get_json_int(root,
"b",
&value),
FPGA_EXCEPTION);
EXPECT_EQ(value, 0);
}
/**
* @test int_err1
* @brief Test: opae_bitstream_get_json_int
* @details If the given name exists,<br>
* but isn't of type integer,<br>
* the fn returns FPGA_EXCEPTION.<br>
*/
TEST_P(bits_utils_c_p, int_err1) {
const char *mdata =
R"mdata({
"a": "str"
})mdata";
json_object *root;
int value = 0;
root = parse(mdata);
ASSERT_NE(root, nullptr);
EXPECT_EQ(opae_bitstream_get_json_int(root,
"a",
&value),
FPGA_EXCEPTION);
EXPECT_EQ(value, 0);
}
/**
* @test double_err0
* @brief Test: opae_bitstream_get_json_double
* @details If the given name doesn't exist,<br>
* the fn returns FPGA_EXCEPTION.<br>
*/
TEST_P(bits_utils_c_p, double_err0) {
const char *mdata =
R"mdata({
"a": 3.14
})mdata";
json_object *root;
double value = 0.0;
root = parse(mdata);
ASSERT_NE(root, nullptr);
EXPECT_EQ(opae_bitstream_get_json_double(root,
"b",
&value),
FPGA_EXCEPTION);
EXPECT_EQ(value, 0.0);
}
/**
* @test double_err1
* @brief Test: opae_bitstream_get_json_double
* @details If the given name exists,<br>
* but isn't of type double,<br>
* the fn returns FPGA_EXCEPTION.<br>
*/
TEST_P(bits_utils_c_p, double_err1) {
const char *mdata =
R"mdata({
"a": "str"
})mdata";
json_object *root;
double value = 0.0;
root = parse(mdata);
ASSERT_NE(root, nullptr);
EXPECT_EQ(opae_bitstream_get_json_double(root,
"a",
&value),
FPGA_EXCEPTION);
EXPECT_EQ(value, 0.0);
}
/**
* @test double_err2
* @brief Test: opae_bitstream_get_json_double
* @details If the given name exists,<br>
* but isn't of type double,<br>
* the fn returns FPGA_EXCEPTION.<br>
*/
TEST_P(bits_utils_c_p, double_err2) {
const char *mdata =
R"mdata({
"a": 42
})mdata";
json_object *root;
double value = 0.0;
root = parse(mdata);
ASSERT_NE(root, nullptr);
EXPECT_EQ(opae_bitstream_get_json_double(root,
"a",
&value),
FPGA_EXCEPTION);
EXPECT_EQ(value, 0.0);
}
/**
* @test invalid_chars0
* @brief Test: opae_bitstream_path_invalid_chars
* @details Given a path that contains non-printable chars,<br>
* the fn returns true.<br>
*/
TEST_P(bits_utils_c_p, invalid_chars0) {
const char *p;
p = "\x01\x05xyz.gbs";
EXPECT_TRUE(opae_bitstream_path_invalid_chars(p, strlen(p)));
}
/**
* @test invalid_chars1
* @brief Test: opae_bitstream_path_invalid_chars
* @details Given a path that contains URL encoding,<br>
* the fn returns true.<br>
*/
TEST_P(bits_utils_c_p, invalid_chars1) {
const char *p;
p = "my%2E.gbs";
EXPECT_TRUE(opae_bitstream_path_invalid_chars(p, strlen(p)));
}
/**
* @test invalid_chars2
* @brief Test: opae_bitstream_path_invalid_chars
* @details Given a path that contains no invalid chars,<br>
* the fn returns false.<br>
*/
TEST_P(bits_utils_c_p, invalid_chars2) {
const char *p;
p = "abc.gbs";
EXPECT_FALSE(opae_bitstream_path_invalid_chars(p, strlen(p)));
}
/**
* @test not_file0
* @brief Test: opae_bitstream_path_not_file
* @details Given a path to a file that doesn't exist,<br>
* the fn returns true.<br>
*/
TEST_P(bits_utils_c_p, not_file0) {
EXPECT_TRUE(opae_bitstream_path_not_file("doesntexist"));
}
/**
* @test not_file1
* @brief Test: opae_bitstream_path_not_file
* @details Given a path to a directory,<br>
* the fn returns true.<br>
*/
TEST_P(bits_utils_c_p, not_file1) {
EXPECT_TRUE(opae_bitstream_path_not_file("/"));
}
/**
* @test not_file2
* @brief Test: opae_bitstream_path_not_file
* @details Given a path to valid file,<br>
* the fn returns false.<br>
*/
TEST_P(bits_utils_c_p, not_file2) {
char tmpfile[20];
strcpy(tmpfile, "tmp-XXXXXX.gbs");
close(mkstemps(tmpfile, 4));
EXPECT_FALSE(opae_bitstream_path_not_file(tmpfile));
unlink(tmpfile);
}
/**
* @test dotdot0
* @brief Test: opae_bitstream_path_contains_dotdot
* @details Given a path that contains a reference to<br>
* the special directory designator ..<br>
* the fn returns true.<br>
*/
TEST_P(bits_utils_c_p, dotdot0) {
EXPECT_TRUE(opae_bitstream_path_contains_dotdot("..", 2));
EXPECT_TRUE(opae_bitstream_path_contains_dotdot("../", 3));
EXPECT_TRUE(opae_bitstream_path_contains_dotdot("../abc.gbs", 10));
EXPECT_TRUE(opae_bitstream_path_contains_dotdot("my/../abc.gbs", 13));
EXPECT_TRUE(opae_bitstream_path_contains_dotdot("my/..", 5));
}
/**
* @test dotdot1
* @brief Test: opae_bitstream_path_contains_dotdot
* @details Given a path that contains the character sequence '..',<br>
* if that character sequence does not designate the parent dir,<br>
* the fn returns false.<br>
*/
TEST_P(bits_utils_c_p, dotdot1) {
EXPECT_FALSE(opae_bitstream_path_contains_dotdot("my..gbs", 7));
}
/**
* @test symlink0
* @brief Test: opae_bitstream_path_contains_symlink
* @details If the given path string is empty,<br>
* then the fn returns true.<br>
*/
TEST_P(bits_utils_c_p, symlink0) {
EXPECT_TRUE(opae_bitstream_path_contains_symlink("", 0));
}
/**
* @test symlink1
* @brief Test: opae_bitstream_path_contains_symlink
* @details If the given file name doesn't exist,<br>
* then the fn returns true.<br>
*/
TEST_P(bits_utils_c_p, symlink1) {
EXPECT_TRUE(opae_bitstream_path_contains_symlink("doesntexist", 11));
}
/**
* @test symlink2
* @brief Test: opae_bitstream_path_contains_symlink
* @details If the given file name exists,<br>
* and it does not contain any / characters,<br>
* and it is a symlink,<br>
* then the fn returns true.<br>
*/
TEST_P(bits_utils_c_p, symlink2) {
char tmpfile[20];
strcpy(tmpfile, "tmp-XXXXXX.gbs");
close(mkstemps(tmpfile, 4));
ASSERT_EQ(symlink(tmpfile, "mylink"), 0);
EXPECT_TRUE(opae_bitstream_path_contains_symlink("mylink", 6));
unlink("mylink");
unlink(tmpfile);
}
/**
* @test symlink3
* @brief Test: opae_bitstream_path_contains_symlink
* @details If the given file name exists,<br>
* and it does not contain a / character in position 0,<br>
* and there is a symlink in any of the path components,<br>
* then the fn returns true.<br>
*/
TEST_P(bits_utils_c_p, symlink3) {
char tmpfile[20];
strcpy(tmpfile, "tmp-XXXXXX.gbs");
close(mkstemps(tmpfile, 4));
std::string s;
EXPECT_EQ(std::system("rm -rf bar"), 0);
// bar/baz/foo -> tmpfile
ASSERT_EQ(mkdir("bar", 0755), 0);
ASSERT_EQ(mkdir("bar/baz", 0755), 0);
s = std::string("../../") + std::string(tmpfile);
ASSERT_EQ(symlink(s.c_str(), "bar/baz/foo"), 0);
EXPECT_TRUE(opae_bitstream_path_contains_symlink("bar/baz/foo", 11));
ASSERT_EQ(unlink("bar/baz/foo"), 0);
ASSERT_EQ(rmdir("bar/baz"), 0);
ASSERT_EQ(rmdir("bar"), 0);
// bar/baz -> ../
ASSERT_EQ(mkdir("bar", 0755), 0);
ASSERT_EQ(symlink("..", "bar/baz"), 0);
s = std::string("bar/baz/") + std::string(tmpfile);
EXPECT_TRUE(opae_bitstream_path_contains_symlink(s.c_str(), strlen(s.c_str())));
ASSERT_EQ(unlink("bar/baz"), 0);
ASSERT_EQ(rmdir("bar"), 0);
// bar -> blah which contains baz, which contains the config file
ASSERT_EQ(mkdir("blah", 0755), 0);
ASSERT_EQ(mkdir("blah/baz", 0755), 0);
s = std::string("blah/baz/") + std::string(tmpfile);
ASSERT_EQ(rename(tmpfile, s.c_str()), 0);
ASSERT_EQ(symlink("blah", "bar"), 0);
s = std::string("bar/baz/") + std::string(tmpfile);
EXPECT_TRUE(opae_bitstream_path_contains_symlink(s.c_str(), strlen(s.c_str())));
ASSERT_EQ(rename(s.c_str(), tmpfile), 0);
ASSERT_EQ(rmdir("blah/baz"), 0);
ASSERT_EQ(rmdir("blah"), 0);
ASSERT_EQ(unlink("bar"), 0);
ASSERT_EQ(unlink(tmpfile), 0);
}
/**
* @test symlink4
* @brief Test: opae_bitstream_path_contains_symlink
* @details If the given file name exists,<br>
* and it contains a / character in position 0,<br>
* and there is a symlink in any of the path components,<br>
* then the fn returns true.<br>
*/
TEST_P(bits_utils_c_p, symlink4) {
char tmpfile[20];
strcpy(tmpfile, "tmp-XXXXXX.gbs");
close(mkstemps(tmpfile, 4));
std::string s;
char *d = get_current_dir_name();
ASSERT_NE(d, nullptr);
// /current/dir/foo -> cfg file
ASSERT_EQ(symlink(tmpfile, "foo"), 0);
s = std::string(d) + std::string("/foo");
EXPECT_TRUE(opae_bitstream_path_contains_symlink(s.c_str(), strlen(s.c_str())));
ASSERT_EQ(unlink("foo"), 0);
ASSERT_EQ(unlink(tmpfile), 0);
free(d);
}
/**
* @test symlink5
* @brief Test: opae_bitstream_path_contains_symlink
* @details If the given file name exists and is a regular file,<br>
* then the fn returns false.<br>
*/
TEST_P(bits_utils_c_p, symlink5) {
char tmpfile[20];
strcpy(tmpfile, "tmp-XXXXXX.gbs");
close(mkstemps(tmpfile, 4));
EXPECT_FALSE(opae_bitstream_path_contains_symlink(tmpfile, strlen(tmpfile)));
ASSERT_EQ(unlink(tmpfile), 0);
}
/**
* @test is_valid0
* @brief Test: opae_bitstream_path_is_valid
* @details If the given path pointer is NULL or<br>
* points to the empty string,<br>
* then the fn returns false.<br>
*/
TEST_P(bits_utils_c_p, is_valid0) {
EXPECT_FALSE(opae_bitstream_path_is_valid(NULL, 0));
EXPECT_FALSE(opae_bitstream_path_is_valid("", 0));
}
/**
* @test is_valid1
* @brief Test: opae_bitstream_path_is_valid
* @details If the given path contains non-printable characters,<br>
* then the fn returns false.<br>
*/
TEST_P(bits_utils_c_p, is_valid1) {
const char *p = "\x01ijk.gbs";
EXPECT_FALSE(opae_bitstream_path_is_valid(p, 0));
}
/**
* @test is_valid2
* @brief Test: opae_bitstream_path_is_valid
* @details If the given path doesn't exist,<br>
* then the fn returns false.<br>
*/
TEST_P(bits_utils_c_p, is_valid2) {
EXPECT_FALSE(opae_bitstream_path_is_valid("doesntexist", 0));
}
/**
* @test is_valid3
* @brief Test: opae_bitstream_path_is_valid
* @details If the given flags parameter does not contain<br>
* OPAE_BITSTREAM_PATH_NO_PARENT,<br>
* and the special parent directory indicator ..<br>
* appears in the path, then the fn returns true.<br>
*/
TEST_P(bits_utils_c_p, is_valid3) {
char tmpfile[32];
EXPECT_EQ(std::system("rm -rf bar"), 0);
ASSERT_EQ(mkdir("bar", 0755), 0);
strcpy(tmpfile, "tmp-XXXXXX.gbs");
close(mkstemps(tmpfile, 4));
std::string s = std::string("bar/../") + std::string(tmpfile);
EXPECT_TRUE(opae_bitstream_path_is_valid(s.c_str(), 0));
ASSERT_EQ(unlink(tmpfile), 0);
ASSERT_EQ(rmdir("bar"), 0);
}
/**
* @test is_valid4
* @brief Test: opae_bitstream_path_is_valid
* @details If the given flags parameter contains<br>
* OPAE_BITSTREAM_PATH_NO_PARENT,<br>
* and the special parent directory indicator ..<br>
* appears in the path, then the fn returns false.<br>
*/
TEST_P(bits_utils_c_p, is_valid4) {
char tmpfile[32];
EXPECT_EQ(std::system("rm -rf bar"), 0);
ASSERT_EQ(mkdir("bar", 0755), 0);
strcpy(tmpfile, "tmp-XXXXXX.gbs");
close(mkstemps(tmpfile, 4));
std::string s = std::string("bar/../") + std::string(tmpfile);
EXPECT_FALSE(opae_bitstream_path_is_valid(s.c_str(),
OPAE_BITSTREAM_PATH_NO_PARENT));
ASSERT_EQ(unlink(tmpfile), 0);
ASSERT_EQ(rmdir("bar"), 0);
}
/**
* @test is_valid5
* @brief Test: opae_bitstream_path_is_valid
* @details If the given flags parameter does not contain<br>
* OPAE_BITSTREAM_PATH_NO_SYMLINK,<br>
* and the path contains a symlink component,<br>
* then the fn returns true.<br>
*/
TEST_P(bits_utils_c_p, is_valid5) {
char tmpfile[20];
strcpy(tmpfile, "tmp-XXXXXX.gbs");
close(mkstemps(tmpfile, 4));
ASSERT_EQ(symlink(tmpfile, "foo"), 0);
EXPECT_TRUE(opae_bitstream_path_is_valid("foo", 0));
ASSERT_EQ(unlink("foo"), 0);
ASSERT_EQ(unlink(tmpfile), 0);
}
/**
* @test is_valid6
* @brief Test: opae_bitstream_path_is_valid
* @details If the given flags parameter contains<br>
* OPAE_BITSTREAM_PATH_NO_SYMLINK,<br>
* and the path contains a symlink component,<br>
* then the fn returns false.<br>
*/
TEST_P(bits_utils_c_p, is_valid6) {
char tmpfile[20];
strcpy(tmpfile, "tmp-XXXXXX.gbs");
close(mkstemps(tmpfile, 4));
ASSERT_EQ(symlink(tmpfile, "foo"), 0);
EXPECT_FALSE(opae_bitstream_path_is_valid("foo",
OPAE_BITSTREAM_PATH_NO_SYMLINK));
ASSERT_EQ(unlink("foo"), 0);
ASSERT_EQ(unlink(tmpfile), 0);
}
INSTANTIATE_TEST_CASE_P(bits_utils_c, bits_utils_c_p,
::testing::ValuesIn(test_platform::platforms({})));
class mock_bits_utils_c_p : public bits_utils_c_p {};
/**
* @test string_err2
* @brief Test: opae_bitstream_get_json_string
* @details If malloc fails,<br>
* the fn returns FPGA_NO_MEMORY.<br>
*/
TEST_P(mock_bits_utils_c_p, string_err2) {
const char *mdata =
R"mdata({
"a": "str"
})mdata";
json_object *root;
char *value = nullptr;
root = parse(mdata);
ASSERT_NE(root, nullptr);
system_->invalidate_malloc(0, "opae_bitstream_get_json_string");
EXPECT_EQ(opae_bitstream_get_json_string(root,
"a",
&value),
FPGA_NO_MEMORY);
EXPECT_EQ(value, nullptr);
}
INSTANTIATE_TEST_CASE_P(bits_utils_c, mock_bits_utils_c_p,
::testing::ValuesIn(test_platform::mock_platforms({})));