Skip to content

Commit e2da159

Browse files
committed
int: Switch to spans for some exif manipulation, fixing warnings (#4689)
We recently started getting some CI failures on the "bleeding edge" test that uses the new gcc 14, seems that the newest cut of that compiler was giving us new warnings where it suspected we were writing past the valid bounds of some memory areas, but it was very hard to decipher the circumstances and either find the bug or convince myself that is was a false positive. To help me debug it, I switched some of the internals of exif.cpp that passed around raw pointers in very hard-to-validate ways to instead use spans with explicit lengths, in the hopes that the debug-mode bounds checking it provides would allow me to more easily track down the problem. Now it no longer gets compiler warnings. Did I fix a very subtle bug in the process? Does the reformulation with spans make it easier for the compiler to verify that we aren't stomping on memory, and that suppresses the warning? Either way, it now passes all CI tests and I think the new code is safer and aligned with our strategy of slowly changing raw unbounded pointers to using spans in order to make these kinds of bugs harder to introduce. Signed-off-by: Larry Gritz <[email protected]>
1 parent 8b9f1b7 commit e2da159

File tree

4 files changed

+51
-32
lines changed

4 files changed

+51
-32
lines changed

src/include/OpenImageIO/span.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -575,6 +575,17 @@ as_writable_bytes(T* ptr, size_t len) noexcept
575575

576576

577577

578+
/// Convert a reference to a single variable to a const byte span of that
579+
/// object's memory.
580+
template<typename T>
581+
span<const std::byte>
582+
as_bytes_ref(const T& ref) noexcept
583+
{
584+
return make_cspan(reinterpret_cast<const std::byte*>(&ref), sizeof(T));
585+
}
586+
587+
588+
578589
/// Try to copy `n` items of type `T` from `src[srcoffset...]` to
579590
/// `dst[dstoffset...]`. Don't read or write outside the respective span
580591
/// boundaries. Return the number of items actually copied, which should be

src/libOpenImageIO/exif-canon.cpp

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -990,8 +990,9 @@ encode_indexed_tag (int tifftag, TIFFDataType tifftype, // TIFF tag and type
990990
}
991991
}
992992
if (anyfound)
993-
append_tiff_dir_entry (dirs, data, tifftag, tifftype,
994-
array.size(), array.data(), offset_correction);
993+
append_tiff_dir_entry(dirs, data, tifftag, tifftype,
994+
array.size(), as_bytes(make_cspan(array)),
995+
offset_correction);
995996
}
996997

997998

@@ -1015,8 +1016,9 @@ encode_canon_makernote (std::vector<char>& data,
10151016
d = param->get_ustring().c_str();
10161017
count = param->get_ustring().size() + 1;
10171018
}
1018-
append_tiff_dir_entry (makerdirs, data, t.tifftag, t.tifftype,
1019-
count, d, offset_correction);
1019+
append_tiff_dir_entry(makerdirs, data, t.tifftag, t.tifftype,
1020+
count, as_bytes(d, count),
1021+
offset_correction);
10201022
}
10211023
}
10221024

src/libOpenImageIO/exif.cpp

Lines changed: 33 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -949,22 +949,24 @@ append_tiff_dir_entry_integer(const ParamValue& p,
949949
{
950950
T i;
951951
switch (p.type().basetype) {
952-
case TypeDesc::UINT: i = (T) * (unsigned int*)p.data(); break;
953-
case TypeDesc::INT: i = (T) * (int*)p.data(); break;
954-
case TypeDesc::UINT16: i = (T) * (unsigned short*)p.data(); break;
955-
case TypeDesc::INT16: i = (T) * (short*)p.data(); break;
952+
case TypeDesc::UINT: i = static_cast<T>(p.get<uint32_t>()); break;
953+
case TypeDesc::INT: i = static_cast<T>(p.get<int32_t>()); break;
954+
case TypeDesc::UINT16: i = static_cast<T>(p.get<uint16_t>()); break;
955+
case TypeDesc::INT16: i = static_cast<T>(p.get<int16_t>()); break;
956+
case TypeDesc::UINT8: i = static_cast<T>(p.get<uint8_t>()); break;
957+
case TypeDesc::INT8: i = static_cast<T>(p.get<int8_t>()); break;
956958
case TypeDesc::STRING: {
957959
if (Strutil::string_is_int(p.get_ustring())) {
958-
i = (T)p.get_int();
960+
i = static_cast<T>(p.get_int());
959961
break;
960962
} else {
961963
return false;
962964
}
963965
}
964966
default: return false;
965967
}
966-
append_tiff_dir_entry(dirs, data, tag, type, 1, &i, offset_correction, 0,
967-
endianreq);
968+
append_tiff_dir_entry(dirs, data, tag, type, 1, as_bytes_ref(i),
969+
offset_correction, 0, endianreq);
968970
return true;
969971
}
970972

@@ -992,30 +994,31 @@ encode_exif_entry(const ParamValue& p, int tag, std::vector<TIFFDirEntry>& dirs,
992994
ustring s = *(const ustring*)p.data();
993995
if (s.size()) {
994996
int len = size_t(s.size()) + 1;
995-
append_tiff_dir_entry(dirs, data, tag, type, len, s.data(),
997+
append_tiff_dir_entry(dirs, data, tag, type, len,
998+
as_bytes(s.data(), len),
996999
offset_correction, 0, endianreq);
9971000
}
9981001
return;
9991002
}
10001003
break;
10011004
case TIFF_RATIONAL:
10021005
if (element == TypeDesc::FLOAT) {
1003-
unsigned int* rat = OIIO_ALLOCA(unsigned int, 2 * count);
1004-
const float* f = (const float*)p.data();
1006+
auto rat = OIIO_ALLOCA_SPAN(unsigned int, 2 * count);
1007+
const float* f = (const float*)p.data();
10051008
for (size_t i = 0; i < count; ++i)
10061009
float_to_rational(f[i], rat[2 * i], rat[2 * i + 1]);
1007-
append_tiff_dir_entry(dirs, data, tag, type, count, rat,
1010+
append_tiff_dir_entry(dirs, data, tag, type, count, as_bytes(rat),
10081011
offset_correction, 0, endianreq);
10091012
return;
10101013
}
10111014
break;
10121015
case TIFF_SRATIONAL:
10131016
if (element == TypeDesc::FLOAT) {
1014-
int* rat = OIIO_ALLOCA(int, 2 * count);
1017+
auto rat = OIIO_ALLOCA_SPAN(int, 2 * count);
10151018
const float* f = (const float*)p.data();
10161019
for (size_t i = 0; i < count; ++i)
10171020
float_to_rational(f[i], rat[2 * i], rat[2 * i + 1]);
1018-
append_tiff_dir_entry(dirs, data, tag, type, count, rat,
1021+
append_tiff_dir_entry(dirs, data, tag, type, count, as_bytes(rat),
10191022
offset_correction, 0, endianreq);
10201023
return;
10211024
}
@@ -1081,7 +1084,7 @@ pvt::decode_ifd(cspan<uint8_t> buf, size_t ifd_offset, ImageSpec& spec,
10811084
void
10821085
pvt::append_tiff_dir_entry(std::vector<TIFFDirEntry>& dirs,
10831086
std::vector<char>& data, int tag, TIFFDataType type,
1084-
size_t count, const void* mydata,
1087+
size_t count, cspan<std::byte> mydata,
10851088
size_t offset_correction, size_t offset_override,
10861089
OIIO::endian endianreq)
10871090
{
@@ -1095,16 +1098,19 @@ pvt::append_tiff_dir_entry(std::vector<TIFFDirEntry>& dirs,
10951098
if (len <= 4) {
10961099
dir.tdir_offset = 0;
10971100
data_in_offset = true;
1098-
if (mydata) {
1101+
if (mydata.size()) {
10991102
ptr = (char*)&dir.tdir_offset;
1100-
memcpy(ptr, mydata, len);
1103+
memcpy(ptr, mydata.data(), mydata.size());
11011104
}
11021105
} else {
1103-
if (mydata) {
1106+
if (mydata.size()) {
11041107
// Add to the data vector and use its offset
11051108
size_t oldsize = data.size();
11061109
dir.tdir_offset = data.size() - offset_correction;
1107-
data.insert(data.end(), (char*)mydata, (char*)mydata + len);
1110+
OIIO_DASSERT(len == mydata.size());
1111+
data.insert(data.end(),
1112+
reinterpret_cast<const char*>(mydata.begin()),
1113+
reinterpret_cast<const char*>(mydata.end()));
11081114
ptr = &data[oldsize];
11091115
} else {
11101116
// An offset override was given, use that, it means that data
@@ -1365,22 +1371,22 @@ encode_exif(const ImageSpec& spec, std::vector<char>& blob,
13651371
if (exifdirs.size() || makerdirs.size()) {
13661372
// Add some required Exif tags that wouldn't be in the spec
13671373
append_tiff_dir_entry(exifdirs, blob, EXIF_EXIFVERSION, TIFF_UNDEFINED,
1368-
4, "0230", tiffstart, 0, endianreq);
1374+
4, as_bytes("0230", 4), tiffstart, 0, endianreq);
13691375
append_tiff_dir_entry(exifdirs, blob, EXIF_FLASHPIXVERSION,
1370-
TIFF_UNDEFINED, 4, "0100", tiffstart, 0,
1371-
endianreq);
1376+
TIFF_UNDEFINED, 4, as_bytes("0100", 4), tiffstart,
1377+
0, endianreq);
13721378
static char componentsconfig[] = { 1, 2, 3, 0 };
13731379
append_tiff_dir_entry(exifdirs, blob, EXIF_COMPONENTSCONFIGURATION,
1374-
TIFF_UNDEFINED, 4, componentsconfig, tiffstart, 0,
1375-
endianreq);
1380+
TIFF_UNDEFINED, 4, as_bytes(componentsconfig, 4),
1381+
tiffstart, 0, endianreq);
13761382
}
13771383

13781384
// If any GPS info was found, add a version tag to the GPS fields.
13791385
if (gpsdirs.size()) {
13801386
// Add some required Exif tags that wouldn't be in the spec
13811387
static char ver[] = { 2, 2, 0, 0 };
13821388
append_tiff_dir_entry(gpsdirs, blob, GPSTAG_VERSIONID, TIFF_BYTE, 4,
1383-
&ver, tiffstart, 0, endianreq);
1389+
as_bytes(ver, 4), tiffstart, 0, endianreq);
13841390
}
13851391

13861392
// Compute offsets:
@@ -1422,22 +1428,22 @@ encode_exif(const ImageSpec& spec, std::vector<char>& blob,
14221428
OIIO_ASSERT(exifdirs.size());
14231429
// unsigned int size = (unsigned int) makerdirs_offset;
14241430
append_tiff_dir_entry(exifdirs, blob, EXIF_MAKERNOTE, TIFF_BYTE,
1425-
makerdirs_size, nullptr, 0, makerdirs_offset,
1431+
makerdirs_size, {}, 0, makerdirs_offset,
14261432
endianreq);
14271433
}
14281434

14291435
// If any Exif info was found, add a Exif IFD tag to the TIFF dirs
14301436
if (exifdirs.size()) {
14311437
unsigned int size = (unsigned int)exifdirs_offset;
14321438
append_tiff_dir_entry(tiffdirs, blob, TIFFTAG_EXIFIFD, TIFF_LONG, 1,
1433-
&size, tiffstart, 0, endianreq);
1439+
as_bytes_ref(size), tiffstart, 0, endianreq);
14341440
}
14351441

14361442
// If any GPS info was found, add a GPS IFD tag to the TIFF dirs
14371443
if (gpsdirs.size()) {
14381444
unsigned int size = (unsigned int)gpsdirs_offset;
14391445
append_tiff_dir_entry(tiffdirs, blob, TIFFTAG_GPSIFD, TIFF_LONG, 1,
1440-
&size, tiffstart, 0, endianreq);
1446+
as_bytes_ref(size), tiffstart, 0, endianreq);
14411447
}
14421448

14431449
// All the tag dirs need to be sorted

src/libOpenImageIO/exif.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ cspan<ExplanationTableEntry> canon_explanation_table ();
133133
void append_tiff_dir_entry (std::vector<TIFFDirEntry> &dirs,
134134
std::vector<char> &data,
135135
int tag, TIFFDataType type, size_t count,
136-
const void *mydata, size_t offset_correction,
136+
cspan<std::byte> mydata, size_t offset_correction,
137137
size_t offset_override = 0,
138138
OIIO::endian endianreq = OIIO::endian::native);
139139

0 commit comments

Comments
 (0)