/*
* This file is part of the ZoneMinder Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "zm_catch2.h"
#include "zm_buffer.h"
#include "zm_remote_camera_http.h"
#include
#include
#include
// A Remote/HTTP camera's Content-Length decides how much zmc reads into a Buffer. Buffer did
// its size arithmetic in int, so a length near 2^31 wrapped the allocation to a small block
// and the read or the copy ran past it. refs GHSA-x959-xrc9-89p8
namespace {
struct Pipe {
int fds[2] = {-1, -1};
Pipe() { REQUIRE(pipe(fds) == 0); }
~Pipe() {
if (fds[0] >= 0) close(fds[0]);
if (fds[1] >= 0) close(fds[1]);
}
void Write(const std::string &s) { REQUIRE(write(fds[1], s.data(), s.size()) == static_cast(s.size())); }
};
} // namespace
TEST_CASE("Buffer::read_into reads what arrives", "[Buffer]") {
Pipe p;
p.Write("hello");
Buffer buffer(4);
REQUIRE(buffer.read_into(p.fds[0], 16) == 5);
REQUIRE(buffer.size() == 5);
REQUIRE(std::memcmp(buffer.head(), "hello", 5) == 0);
}
TEST_CASE("Buffer::read_into refuses a read the buffer cannot hold", "[Buffer]") {
Pipe p;
p.Write("hello");
Buffer buffer(16);
// More than an int can count: before the fix this allocated about 2 GiB and then read.
REQUIRE(buffer.read_into(p.fds[0], static_cast(INT_MAX) + 1u) < 0);
REQUIRE(buffer.size() == 0);
// The data is still there for a sensible read.
REQUIRE(buffer.read_into(p.fds[0], 16) == 5);
}
TEST_CASE("Buffer::expand keeps the data when it moves or grows", "[Buffer]") {
Buffer buffer(8);
buffer.append("abcdefgh", 8);
buffer.consume(6); // "gh" left, with 6 bytes of head space
// Fits once the data is moved to the front of the allocation.
buffer.expand(5);
REQUIRE(buffer.size() == 2);
REQUIRE(std::memcmp(buffer.head(), "gh", 2) == 0);
REQUIRE(buffer.tail() == buffer.head() + 2);
// Needs a bigger allocation.
buffer.expand(100);
REQUIRE(buffer.size() == 2);
REQUIRE(std::memcmp(buffer.head(), "gh", 2) == 0);
buffer.append(std::string(100, 'x').c_str(), 100);
REQUIRE(buffer.size() == 102);
}
TEST_CASE("RemoteCameraHttp::ParseContentLength bounds the announced length", "[RemoteCameraHttp]") {
const unsigned long long image_size = 640 * 480 * 4;
REQUIRE(RemoteCameraHttp::ParseContentLength("1000", image_size) == 1000);
REQUIRE(RemoteCameraHttp::ParseContentLength("0", image_size) == 0);
REQUIRE(RemoteCameraHttp::ParseContentLength("1000\r\n", image_size) == 1000);
// A frame can't be bigger than twice its raw size.
REQUIRE(RemoteCameraHttp::ParseContentLength("2457600", image_size) == 2457600);
REQUIRE(RemoteCameraHttp::ParseContentLength("50000000", image_size) < 0);
REQUIRE(RemoteCameraHttp::ParseContentLength("2147483647", image_size) < 0);
REQUIRE(RemoteCameraHttp::ParseContentLength("99999999999999999999", image_size) < 0);
REQUIRE(RemoteCameraHttp::ParseContentLength("-5", image_size) < 0);
REQUIRE(RemoteCameraHttp::ParseContentLength("abc", image_size) < 0);
REQUIRE(RemoteCameraHttp::ParseContentLength("", image_size) < 0);
// A huge configured frame still yields a length an int can hold.
REQUIRE(RemoteCameraHttp::ParseContentLength("2147483647", 4000000000ull) < 0);
}