Skip to content

Commit c6bbc03

Browse files
committedMar 7, 2023
Throw an exception if trying to write a JPEG APP1 segment that's too large
jpeg_with_full_app_segment.jpg is an image taken with a Samsung A32 with a nearly-full APP1 segment due to a 64,000 byte thumbnail: $ exiftool -v3 jpeg_with_exif_full_app1_segment.jpg <snip> JPEG APP1 (65072 bytes): <snip> | 5) ThumbnailLength = 64000 Bug: 263747161 Test: ./gradlew :exifinterface:exifinterface:connectedAndroidTest Change-Id: Id421cd36d974ad4f29a2c6d6c730d99d8986d917
1 parent ac3737b commit c6bbc03

File tree

4 files changed

+44
-0
lines changed

4 files changed

+44
-0
lines changed
 

‎exifinterface/exifinterface/build.gradle

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ dependencies {
99
implementation("androidx.annotation:annotation:1.2.0")
1010

1111
androidTestImplementation(libs.testExtJunit)
12+
androidTestImplementation(libs.testExtTruth)
1213
androidTestImplementation(libs.testCore)
1314
androidTestImplementation(libs.testRunner)
1415
androidTestImplementation(libs.testRules)

‎exifinterface/exifinterface/src/androidTest/java/androidx/exifinterface/media/ExifInterfaceTest.java

+30
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,13 @@
1818

1919
import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
2020

21+
import static com.google.common.truth.Truth.assertThat;
22+
2123
import static org.junit.Assert.assertEquals;
2224
import static org.junit.Assert.assertFalse;
2325
import static org.junit.Assert.assertNotNull;
2426
import static org.junit.Assert.assertNull;
27+
import static org.junit.Assert.assertThrows;
2528
import static org.junit.Assert.assertTrue;
2629
import static org.junit.Assert.fail;
2730

@@ -64,6 +67,7 @@
6467
import java.io.OutputStream;
6568
import java.nio.ByteBuffer;
6669
import java.nio.charset.Charset;
70+
import java.util.Arrays;
6771
import java.util.HashMap;
6872
import java.util.Objects;
6973
import java.util.Random;
@@ -88,6 +92,8 @@ public class ExifInterfaceTest {
8892
private static final String JPEG_WITH_EXIF_BYTE_ORDER_II = "jpeg_with_exif_byte_order_ii.jpg";
8993
private static final String JPEG_WITH_EXIF_BYTE_ORDER_MM = "jpeg_with_exif_byte_order_mm.jpg";
9094
private static final String JPEG_WITH_EXIF_INVALID_OFFSET = "jpeg_with_exif_invalid_offset.jpg";
95+
private static final String JPEG_WITH_EXIF_FULL_APP1_SEGMENT =
96+
"jpeg_with_exif_full_app1_segment.jpg";
9197

9298
private static final String DNG_WITH_EXIF_WITH_XMP = "dng_with_exif_with_xmp.dng";
9399
private static final String JPEG_WITH_EXIF_WITH_XMP = "jpeg_with_exif_with_xmp.jpg";
@@ -110,6 +116,7 @@ public class ExifInterfaceTest {
110116
R.raw.jpeg_with_exif_byte_order_ii,
111117
R.raw.jpeg_with_exif_byte_order_mm,
112118
R.raw.jpeg_with_exif_invalid_offset,
119+
R.raw.jpeg_with_exif_full_app1_segment,
113120
R.raw.dng_with_exif_with_xmp,
114121
R.raw.jpeg_with_exif_with_xmp,
115122
R.raw.png_with_exif_byte_order_ii,
@@ -126,6 +133,7 @@ public class ExifInterfaceTest {
126133
JPEG_WITH_EXIF_BYTE_ORDER_II,
127134
JPEG_WITH_EXIF_BYTE_ORDER_MM,
128135
JPEG_WITH_EXIF_INVALID_OFFSET,
136+
JPEG_WITH_EXIF_FULL_APP1_SEGMENT,
129137
DNG_WITH_EXIF_WITH_XMP,
130138
JPEG_WITH_EXIF_WITH_XMP,
131139
PNG_WITH_EXIF_BYTE_ORDER_II,
@@ -466,6 +474,28 @@ public void testJpegWithInvalidOffset() throws Throwable {
466474
writeToFilesWithExif(JPEG_WITH_EXIF_INVALID_OFFSET, R.array.jpeg_with_exif_invalid_offset);
467475
}
468476

477+
// https://issuetracker.google.com/263747161
478+
@Test
479+
@LargeTest
480+
public void testJpegWithFullApp1Segment() throws Throwable {
481+
File srcFile = getFileFromExternalDir(JPEG_WITH_EXIF_FULL_APP1_SEGMENT);
482+
File imageFile = clone(srcFile);
483+
ExifInterface exifInterface = new ExifInterface(imageFile.getAbsolutePath());
484+
// Add a really long string that makes the Exif data too large for the JPEG APP1 segment.
485+
char[] longStringChars = new char[500];
486+
Arrays.fill(longStringChars, 'a');
487+
String longString = new String(longStringChars);
488+
exifInterface.setAttribute(ExifInterface.TAG_MAKE, longString);
489+
490+
IOException expected = assertThrows(IOException.class,
491+
exifInterface::saveAttributes);
492+
assertThat(expected)
493+
.hasCauseThat()
494+
.hasMessageThat()
495+
.contains("exceeds the max size of a JPEG APP1 segment");
496+
assertBitmapsEquivalent(srcFile, imageFile);
497+
}
498+
469499
@Test
470500
@LargeTest
471501
public void testDngWithExifAndXmp() throws Throwable {
Loading

‎exifinterface/exifinterface/src/main/java/androidx/exifinterface/media/ExifInterface.java

+13
Original file line numberDiff line numberDiff line change
@@ -7483,6 +7483,11 @@ private int writeExifSegment(ByteOrderedDataOutputStream dataOutputStream) throw
74837483

74847484
switch (mMimeType) {
74857485
case IMAGE_TYPE_JPEG:
7486+
if (totalSize > 0xFFFF) {
7487+
throw new IllegalStateException(
7488+
"Size of exif data (" + totalSize + " bytes) exceeds the max size of a "
7489+
+ "JPEG APP1 segment (65536 bytes)");
7490+
}
74867491
// Write JPEG specific data (APP1 size, APP1 identifier)
74877492
dataOutputStream.writeUnsignedShort(totalSize);
74887493
dataOutputStream.write(IDENTIFIER_EXIF_APP1);
@@ -8015,10 +8020,18 @@ public void writeInt(int val) throws IOException {
80158020
}
80168021

80178022
public void writeUnsignedShort(int val) throws IOException {
8023+
if (val > 0xFFFF) {
8024+
throw new IllegalArgumentException("val is larger than the maximum value of a "
8025+
+ "16-bit unsigned integer");
8026+
}
80188027
writeShort((short) val);
80198028
}
80208029

80218030
public void writeUnsignedInt(long val) throws IOException {
8031+
if (val > 0xFFFF_FFFFL) {
8032+
throw new IllegalArgumentException("val is larger than the maximum value of a "
8033+
+ "32-bit unsigned integer");
8034+
}
80228035
writeInt((int) val);
80238036
}
80248037
}

0 commit comments

Comments
 (0)