mirror of
https://github.com/ZoneMinder/zoneminder.git
synced 2026-10-13 07:13:21 -04:00
ReadJpeg() stored the JPEG header's width and height in the Image before calling WriteBuffer(). WriteBuffer() reallocates only when the requested size differs from the current one, so it saw no change and kept the existing buffer and linesize, and the scanline loop then decoded every row of a larger file past its end. A File monitor re-reads its source into a monitor-sized image on every capture, so whoever can write that file could overflow zmc's heap. Stored event JPEGs read by zms take the same path. Leave width and height to WriteBuffer(), as DecodeJpeg() already does. FileCamera::Capture() also now refuses a file whose dimensions do not match the monitor, since everything downstream is sized for the monitor. Add a Catch2 case that reads a 256x192 JPEG into a 64x48 image. It segfaulted before this change and passes after. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
116 lines
3.2 KiB
C++
116 lines
3.2 KiB
C++
//
|
|
// ZoneMinder File Camera Class Implementation, $Date$, $Revision$
|
|
// Copyright (C) 2001-2008 Philip Coombes
|
|
//
|
|
// 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, write to the Free Software
|
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
//
|
|
|
|
#include "zm_file_camera.h"
|
|
|
|
#include "zm_packet.h"
|
|
#include <sys/stat.h>
|
|
|
|
FileCamera::FileCamera(
|
|
const Monitor *monitor,
|
|
const char *p_path,
|
|
int p_width,
|
|
int p_height,
|
|
int p_colours,
|
|
int p_brightness,
|
|
int p_contrast,
|
|
int p_hue,
|
|
int p_colour,
|
|
bool p_capture,
|
|
bool p_record_audio)
|
|
: Camera(
|
|
monitor,
|
|
FILE_SRC,
|
|
p_width,
|
|
p_height,
|
|
p_colours,
|
|
ZM_SUBPIX_ORDER_DEFAULT_FOR_COLOUR(p_colours),
|
|
p_brightness,
|
|
p_contrast,
|
|
p_hue,
|
|
p_colour,
|
|
p_capture,
|
|
p_record_audio),
|
|
path(p_path) {
|
|
if (capture) {
|
|
Initialise();
|
|
}
|
|
}
|
|
|
|
FileCamera::~FileCamera() {
|
|
if (capture) {
|
|
Terminate();
|
|
}
|
|
}
|
|
|
|
void FileCamera::Initialise() {
|
|
if (path.empty()) {
|
|
Fatal("No path specified for file image");
|
|
}
|
|
}
|
|
|
|
void FileCamera::Terminate() {
|
|
}
|
|
|
|
int FileCamera::PrimeCapture() {
|
|
getVideoStream();
|
|
Info("Priming capture from %s", path.c_str());
|
|
return 1;
|
|
}
|
|
|
|
int FileCamera::PreCapture() {
|
|
struct stat statbuf = {};
|
|
if (stat(path.c_str(), &statbuf) < 0) {
|
|
Error("Can't stat %s: %s", path.c_str(), strerror(errno));
|
|
return -1;
|
|
}
|
|
bytes += statbuf.st_size;
|
|
|
|
// This waits until 1 second has passed since it was modified. Effectively limiting fps to 60.
|
|
// Which is kinda bogus. If we were writing to this jpg constantly faster than we are monitoring it here
|
|
// we would never break out of this loop
|
|
while ((time(nullptr) - statbuf.st_mtime) < 1) {
|
|
std::this_thread::sleep_for(Milliseconds(100));
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int FileCamera::Capture(std::shared_ptr<ZMPacket> &zm_packet) {
|
|
if (!zm_packet->image) {
|
|
zm_packet->image = new Image(width, height, colours, subpixelorder);
|
|
}
|
|
zm_packet->keyframe = 1;
|
|
zm_packet->codec_type = AVMEDIA_TYPE_VIDEO;
|
|
zm_packet->packet->stream_index = mVideoStreamId;
|
|
zm_packet->stream = mVideoStream;
|
|
if (!zm_packet->image->ReadJpeg(path, colours, subpixelorder)) return -1;
|
|
// Whoever can write the file controls its dimensions. Everything downstream is sized for
|
|
// the monitor, so refuse a file that does not match rather than pass a different size on.
|
|
if (zm_packet->image->Width() != Width() || zm_packet->image->Height() != Height()) {
|
|
Error("File %s is %ux%u but the monitor is %ux%u", path.c_str(),
|
|
zm_packet->image->Width(), zm_packet->image->Height(), Width(), Height());
|
|
return -1;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int FileCamera::PostCapture() {
|
|
return 0;
|
|
}
|