[shotwell] Don't crash on imported photo: Bug #734986
- From: Jim Nelson <jnelson src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [shotwell] Don't crash on imported photo: Bug #734986
- Date: Tue, 19 Aug 2014 21:35:32 +0000 (UTC)
commit 1491418f8cddb2cecc7001a1e9f7109ee8281a8d
Author: Jim Nelson <jim yorba org>
Date: Tue Aug 19 14:34:00 2014 -0700
Don't crash on imported photo: Bug #734986
The user's photo was identified as JFIF but is an incomplete file.
The JFIF sniffer reported it could not identify the file, which is
somewhat incorrect. The RAW sniffer then tried to identify the file
and crashed inside of libraw.
Now when a sniffer can appropriately detect the file is corrupt it
will be reported as such. All GdkSniffers will do this if they (a)
read the entire file and (b) no image was prepared. Because they all
use pre-condition checking to sanity check that the file is their
format (via magic header bytes), this works.
src/Photo.vala | 17 +++++++++++++----
src/photos/BmpSupport.vala | 11 +++++++----
src/photos/GdkSupport.vala | 7 +++++--
src/photos/JfifSupport.vala | 7 +++++--
src/photos/PhotoFileFormat.vala | 4 ++--
src/photos/PhotoFileSniffer.vala | 20 +++++++++++++++++---
src/photos/PngSupport.vala | 11 +++++++----
src/photos/RawSupport.vala | 5 ++++-
src/photos/TiffSupport.vala | 7 +++++--
9 files changed, 65 insertions(+), 24 deletions(-)
---
diff --git a/src/Photo.vala b/src/Photo.vala
index ab449dc..ac324fe 100644
--- a/src/Photo.vala
+++ b/src/Photo.vala
@@ -619,6 +619,12 @@ public abstract class Photo : PhotoSource, Dateable {
interrogator.interrogate();
DetectedPhotoInformation? detected = interrogator.get_detected_photo_information();
+ if (detected == null || interrogator.get_is_photo_corrupted()) {
+ // TODO: Probably should remove from database, but simply exiting for now (prior code
+ // didn't even do this check)
+ return;
+ }
+
bpr.dim = detected.image_dim;
bpr.filesize = info.get_size();
bpr.timestamp = timestamp.tv_sec;
@@ -1149,9 +1155,12 @@ public abstract class Photo : PhotoSource, Dateable {
return ImportResult.DECODE_ERROR;
}
+ if (interrogator.get_is_photo_corrupted())
+ return ImportResult.NOT_AN_IMAGE;
+
// if not detected photo information, unsupported
DetectedPhotoInformation? detected = interrogator.get_detected_photo_information();
- if (detected == null)
+ if (detected == null || detected.file_format == PhotoFileFormat.UNKNOWN)
return ImportResult.UNSUPPORTED_FORMAT;
// copy over supplied MD5s if provided
@@ -1261,7 +1270,7 @@ public abstract class Photo : PhotoSource, Dateable {
try {
interrogator.interrogate();
DetectedPhotoInformation? detected = interrogator.get_detected_photo_information();
- if (detected != null)
+ if (detected != null && !interrogator.get_is_photo_corrupted() && detected.file_format !=
PhotoFileFormat.UNKNOWN)
params.row.master.file_format = detected.file_format;
} catch (Error err) {
debug("Unable to interrogate photo file %s: %s", file.get_path(), err.message);
@@ -1288,7 +1297,7 @@ public abstract class Photo : PhotoSource, Dateable {
PhotoFileInterrogator interrogator = new PhotoFileInterrogator(file, options);
interrogator.interrogate();
detected = interrogator.get_detected_photo_information();
- if (detected == null) {
+ if (detected == null || interrogator.get_is_photo_corrupted()) {
critical("Photo update: %s no longer a recognized image", to_string());
return null;
@@ -2232,7 +2241,7 @@ public abstract class Photo : PhotoSource, Dateable {
}
DetectedPhotoInformation? detected = interrogator.get_detected_photo_information();
- if (detected == null) {
+ if (detected == null || interrogator.get_is_photo_corrupted()) {
critical("file_exif_updated: %s no longer an image", to_string());
return;
diff --git a/src/photos/BmpSupport.vala b/src/photos/BmpSupport.vala
index 546bed2..dbeb64c 100644
--- a/src/photos/BmpSupport.vala
+++ b/src/photos/BmpSupport.vala
@@ -71,14 +71,17 @@ public class BmpSniffer : GdkSniffer {
return true;
}
- public override DetectedPhotoInformation? sniff() throws Error {
+ public override DetectedPhotoInformation? sniff(out bool is_corrupted) throws Error {
+ // Rely on GdkSniffer to detect corruption
+ is_corrupted = false;
+
if (!is_bmp_file(file))
return null;
-
- DetectedPhotoInformation? detected = base.sniff();
+
+ DetectedPhotoInformation? detected = base.sniff(out is_corrupted);
if (detected == null)
return null;
-
+
return (detected.file_format == PhotoFileFormat.BMP) ? detected : null;
}
}
diff --git a/src/photos/GdkSupport.vala b/src/photos/GdkSupport.vala
index 4ca0893..ed2ff63 100644
--- a/src/photos/GdkSupport.vala
+++ b/src/photos/GdkSupport.vala
@@ -34,7 +34,7 @@ public abstract class GdkSniffer : PhotoFileSniffer {
base (file, options);
}
- public override DetectedPhotoInformation? sniff() throws Error {
+ public override DetectedPhotoInformation? sniff(out bool is_corrupted) throws Error {
detected = new DetectedPhotoInformation();
Gdk.PixbufLoader pixbuf_loader = new Gdk.PixbufLoader();
@@ -53,7 +53,7 @@ public abstract class GdkSniffer : PhotoFileSniffer {
// no metadata detected
detected.metadata = null;
}
-
+
if (calc_md5 && detected.metadata != null) {
uint8[]? flattened_sans_thumbnail = detected.metadata.flatten_exif(false);
if (flattened_sans_thumbnail != null && flattened_sans_thumbnail.length > 0)
@@ -102,6 +102,9 @@ public abstract class GdkSniffer : PhotoFileSniffer {
if (calc_md5)
detected.md5 = md5_checksum.get_string();
+ // if size and area are not ready, treat as corrupted file (entire file was read)
+ is_corrupted = !size_ready || !area_prepared;
+
return detected;
}
diff --git a/src/photos/JfifSupport.vala b/src/photos/JfifSupport.vala
index 12ac80a..d721441 100644
--- a/src/photos/JfifSupport.vala
+++ b/src/photos/JfifSupport.vala
@@ -102,11 +102,14 @@ public class JfifSniffer : GdkSniffer {
base (file, options);
}
- public override DetectedPhotoInformation? sniff() throws Error {
+ public override DetectedPhotoInformation? sniff(out bool is_corrupted) throws Error {
+ // Rely on GdkSniffer to detect corruption
+ is_corrupted = false;
+
if (!Jpeg.is_jpeg(file))
return null;
- DetectedPhotoInformation? detected = base.sniff();
+ DetectedPhotoInformation? detected = base.sniff(out is_corrupted);
if (detected == null)
return null;
diff --git a/src/photos/PhotoFileFormat.vala b/src/photos/PhotoFileFormat.vala
index 926254d..2ab2f00 100644
--- a/src/photos/PhotoFileFormat.vala
+++ b/src/photos/PhotoFileFormat.vala
@@ -202,9 +202,9 @@ public enum PhotoFileFormat {
case "tiff":
return PhotoFileFormat.TIFF;
-
+
case "bmp":
- return PhotoFileFormat.BMP;
+ return PhotoFileFormat.BMP;
default:
return PhotoFileFormat.UNKNOWN;
diff --git a/src/photos/PhotoFileSniffer.vala b/src/photos/PhotoFileSniffer.vala
index 8bd6711..3f65ac2 100644
--- a/src/photos/PhotoFileSniffer.vala
+++ b/src/photos/PhotoFileSniffer.vala
@@ -46,7 +46,7 @@ public abstract class PhotoFileSniffer {
calc_md5 = (options & Options.NO_MD5) == 0;
}
- public abstract DetectedPhotoInformation? sniff() throws Error;
+ public abstract DetectedPhotoInformation? sniff(out bool is_corrupted) throws Error;
}
//
@@ -62,6 +62,7 @@ public class PhotoFileInterrogator {
private File file;
private PhotoFileSniffer.Options options;
private DetectedPhotoInformation? detected = null;
+ private bool is_photo_corrupted = false;
public PhotoFileInterrogator(File file,
PhotoFileSniffer.Options options = PhotoFileSniffer.Options.GET_ALL) {
@@ -75,14 +76,27 @@ public class PhotoFileInterrogator {
return detected;
}
+ // Call after interrogate().
+ public bool get_is_photo_corrupted() {
+ return is_photo_corrupted;
+ }
+
public void interrogate() throws Error {
foreach (PhotoFileFormat file_format in PhotoFileFormat.get_supported()) {
PhotoFileSniffer sniffer = file_format.create_sniffer(file, options);
- detected = sniffer.sniff();
- if (detected != null) {
+
+ bool is_corrupted;
+ detected = sniffer.sniff(out is_corrupted);
+ if (detected != null && !is_corrupted) {
assert(detected.file_format == file_format);
break;
+ } else if (is_corrupted) {
+ message("Sniffing halted for %s: potentially corrupted image file", file.get_path());
+ is_photo_corrupted = true;
+ detected = null;
+
+ break;
}
}
}
diff --git a/src/photos/PngSupport.vala b/src/photos/PngSupport.vala
index ffc7faa..2cde6a2 100644
--- a/src/photos/PngSupport.vala
+++ b/src/photos/PngSupport.vala
@@ -69,14 +69,17 @@ public class PngSniffer : GdkSniffer {
return true;
}
- public override DetectedPhotoInformation? sniff() throws Error {
+ public override DetectedPhotoInformation? sniff(out bool is_corrupted) throws Error {
+ // Rely on GdkSniffer to detect corruption
+ is_corrupted = false;
+
if (!is_png_file(file))
return null;
-
- DetectedPhotoInformation? detected = base.sniff();
+
+ DetectedPhotoInformation? detected = base.sniff(out is_corrupted);
if (detected == null)
return null;
-
+
return (detected.file_format == PhotoFileFormat.PNG) ? detected : null;
}
}
diff --git a/src/photos/RawSupport.vala b/src/photos/RawSupport.vala
index bad9572..98bc982 100644
--- a/src/photos/RawSupport.vala
+++ b/src/photos/RawSupport.vala
@@ -163,7 +163,10 @@ public class RawSniffer : PhotoFileSniffer {
base (file, options);
}
- public override DetectedPhotoInformation? sniff() throws Error {
+ public override DetectedPhotoInformation? sniff(out bool is_corrupted) throws Error {
+ // this sniffer doesn't detect corrupted files
+ is_corrupted = false;
+
DetectedPhotoInformation detected = new DetectedPhotoInformation();
GRaw.Processor processor = new GRaw.Processor();
diff --git a/src/photos/TiffSupport.vala b/src/photos/TiffSupport.vala
index decc052..ee8b087 100644
--- a/src/photos/TiffSupport.vala
+++ b/src/photos/TiffSupport.vala
@@ -104,11 +104,14 @@ private class TiffSniffer : GdkSniffer {
base (file, options);
}
- public override DetectedPhotoInformation? sniff() throws Error {
+ public override DetectedPhotoInformation? sniff(out bool is_corrupted) throws Error {
+ // Rely on GdkSniffer to detect corruption
+ is_corrupted = false;
+
if (!is_tiff(file))
return null;
- DetectedPhotoInformation? detected = base.sniff();
+ DetectedPhotoInformation? detected = base.sniff(out is_corrupted);
if (detected == null)
return null;
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]