From 4b207548e09ed0541cb5be70fc61f6353e73bdc4 Mon Sep 17 00:00:00 2001 From: Emilie Yu Date: Fri, 11 Feb 2022 12:12:45 -0800 Subject: [PATCH] CVE-2021-25287,CVE-2021-25288: Fix OOB Read in Jpeg2KDecode --- CHANGES.rst | 8 +++ ...b027452e6988530aa5dabee76eecacb3b79f8a.j2k | Bin 0 -> 335 bytes ...4c83eb92150fb8f1653a697703ae06ae7c4998.j2k | Bin 0 -> 335 bytes ...ca68ff40171fdae983d924e127a721cab2bd50.j2k | Bin 0 -> 335 bytes ...c93af851d3ab9a19e34503626368b2ecde9c03.j2k | Bin 0 -> 3886 bytes Tests/test_file_jpeg2k.py | 18 ++++++ docs/releasenotes/6.2.2.2.rst | 10 +++ docs/releasenotes/index.rst | 1 + src/libImaging/Jpeg2KDecode.c | 58 +++++++++++++++++- 9 files changed, 93 insertions(+), 2 deletions(-) create mode 100644 Tests/images/crash-4fb027452e6988530aa5dabee76eecacb3b79f8a.j2k create mode 100644 Tests/images/crash-7d4c83eb92150fb8f1653a697703ae06ae7c4998.j2k create mode 100644 Tests/images/crash-ccca68ff40171fdae983d924e127a721cab2bd50.j2k create mode 100644 Tests/images/crash-d2c93af851d3ab9a19e34503626368b2ecde9c03.j2k create mode 100644 docs/releasenotes/6.2.2.2.rst diff --git a/CHANGES.rst b/CHANGES.rst index d774aa963..3b1927430 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,6 +2,14 @@ Changelog (Pillow) ================== +6.2.2.2 (date TBD) +------------------ + +- This is the second Pillow release to support Python 2.7 from ActiveState + +- Fix OOB Read in Jpeg2KDecode. CVE 2021-25287, CVE 2021-25288 + [emilieyyu] + 6.2.2.1 (2021-10-08) ------------------ diff --git a/Tests/images/crash-4fb027452e6988530aa5dabee76eecacb3b79f8a.j2k b/Tests/images/crash-4fb027452e6988530aa5dabee76eecacb3b79f8a.j2k new file mode 100644 index 0000000000000000000000000000000000000000..c9bd7fc0a8dfe76af64b373351349c55a1e5de18 GIT binary patch literal 335 zcmZQzVBpCLP*C9IYUg5LU=T?wsVvAUFj4@r8KAT?kj?;d#WFKeih#5N7&Ec6GXfb5 z{K@$_MPL?#AdoAToRXTxzyy*30!APN1E3Mf|NZ|5GU$N03P2KsVL$=M0AUDVVsG3r zgOTxn5CacTfRTZfg@u9fe++}Lf=56=1Plducm(`U0b0uFotT}NQmR{Ks%NBU__0+v5uVf%dk00)ch;R~Sco^cBAMaSeyCLBPTkYfjHv!}sSR()c literal 0 HcmV?d00001 diff --git a/Tests/images/crash-7d4c83eb92150fb8f1653a697703ae06ae7c4998.j2k b/Tests/images/crash-7d4c83eb92150fb8f1653a697703ae06ae7c4998.j2k new file mode 100644 index 0000000000000000000000000000000000000000..fd2f4dd367773b472569bd41715b6892aa4f74b3 GIT binary patch literal 335 zcmZQzVBpCLP*C9IYUg5LU=T?wsVvAUFj4@r8KAT?kj?;d#WFKeih#5N7&Ec6GXfb5 z{K@$_MPL?#AdoAToRXTxzyy*30!APN1E3Mf|NZ|5GU$N03P2KsVL$=M0AUDV5^rQ= zXJq^z#J~d-U}RurVPRnWAHyK5;1LiI0Yd>E9s&PTfR-|PCuS$6lKW-7{-3}A z@(2S%4N%YI<5yT1X8;L~faMQZ*gl^>z`_$>WlejaJCOIozHBX{G&^n;(f8O#>WT$jAqG~@M=OACIoFwC8{l95GQ Z;KspEJPh&6k9REK-H`Btt@iQ%n*cICSFQj6 literal 0 HcmV?d00001 diff --git a/Tests/images/crash-ccca68ff40171fdae983d924e127a721cab2bd50.j2k b/Tests/images/crash-ccca68ff40171fdae983d924e127a721cab2bd50.j2k new file mode 100644 index 0000000000000000000000000000000000000000..c3ad0d6330a7920f58a809e01c8691014ad70fe0 GIT binary patch literal 335 zcmZQzVBpCLP*C9IYUg5LU=T?wsVvAUFj4@r8KAT?kj?;d#WFKeih#5N7&Ec6GXfb5 z{K@$_MPL?#AdoAToRXTxzyy*30!APN1E3Mf|NZ|5GU$N03P2KsVL$=M0AUDVl8}&K zXJq^z#J~d-U}RurVPRnWAHyK5;1LiI0Yd>E9s&PTfR-|PCuS$6lKW-7{-3}A z@(2S%4N%YI<5yT1X8;L~faMQZ*gl^>z`_$>WlejaJCOIozHBX{G&^n;(f8O#>WT$jAqG~@M=OACIoFwC8{l95GQ Z;KspEJPh&6k9REK-H`Btt@iQ%n*jHaS6u)A literal 0 HcmV?d00001 diff --git a/Tests/images/crash-d2c93af851d3ab9a19e34503626368b2ecde9c03.j2k b/Tests/images/crash-d2c93af851d3ab9a19e34503626368b2ecde9c03.j2k new file mode 100644 index 0000000000000000000000000000000000000000..3aadfc3772717bef34360b9138f56365a07728da GIT binary patch literal 3886 zcmV+}57F@dPybN>DF6Tf002M$002M$0000000000002M$002M$00000000000S^H| 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 literal 0 HcmV?d00001 diff --git a/Tests/test_file_jpeg2k.py b/Tests/test_file_jpeg2k.py index 72b374a0b..d3b4c6a78 100644 --- a/Tests/test_file_jpeg2k.py +++ b/Tests/test_file_jpeg2k.py @@ -4,6 +4,8 @@ from PIL import Image, Jpeg2KImagePlugin from .helper import PillowTestCase +import pytest + codecs = dir(Image.core) test_card = Image.open("Tests/images/test-card.png") @@ -210,3 +212,19 @@ class TestFileJpeg2k(PillowTestCase): # Assert self.assertEqual(p.image.size, (640, 480)) + + +@pytest.mark.parametrize( + "test_file", + [ + "Tests/images/crash-4fb027452e6988530aa5dabee76eecacb3b79f8a.j2k", + "Tests/images/crash-7d4c83eb92150fb8f1653a697703ae06ae7c4998.j2k", + "Tests/images/crash-ccca68ff40171fdae983d924e127a721cab2bd50.j2k", + "Tests/images/crash-d2c93af851d3ab9a19e34503626368b2ecde9c03.j2k", + ], +) +def test_crashes(test_file): + with open(test_file, "rb") as f: + with Image.open(f) as im: + # Valgrind should not complain here + im.load() diff --git a/docs/releasenotes/6.2.2.2.rst b/docs/releasenotes/6.2.2.2.rst new file mode 100644 index 000000000..bc9917c6b --- /dev/null +++ b/docs/releasenotes/6.2.2.2.rst @@ -0,0 +1,10 @@ +6.2.2.2 +------- + +Security +======== + +This release addresses several critical CVEs. + +CVE 2021-25287, CVE 2021-25288 has out-of-bounds read in J2kDecode, in +j2ku_graya_la. diff --git a/docs/releasenotes/index.rst b/docs/releasenotes/index.rst index 88ac182ec..3eaab9c1f 100644 --- a/docs/releasenotes/index.rst +++ b/docs/releasenotes/index.rst @@ -6,6 +6,7 @@ Release Notes .. toctree:: :maxdepth: 2 + 6.2.2.2 6.2.2.1 6.2.2 6.2.1 diff --git a/src/libImaging/Jpeg2KDecode.c b/src/libImaging/Jpeg2KDecode.c index f2e437dda..0e61663a5 100644 --- a/src/libImaging/Jpeg2KDecode.c +++ b/src/libImaging/Jpeg2KDecode.c @@ -557,8 +557,9 @@ j2k_decode_entry(Imaging im, ImagingCodecState state) opj_dparameters_t params; OPJ_COLOR_SPACE color_space; j2k_unpacker_t unpack = NULL; - size_t buffer_size = 0; - unsigned n; + size_t buffer_size = 0, tile_bytes = 0; + unsigned n, tile_height, tile_width; + int total_component_width = 0; stream = opj_stream_create(BUFFER_SIZE, OPJ_TRUE); @@ -703,6 +704,59 @@ j2k_decode_entry(Imaging im, ImagingCodecState state) tile_info.x1 = (tile_info.x1 + correction) >> context->reduce; tile_info.y1 = (tile_info.y1 + correction) >> context->reduce; + /* Check the tile bounds; if the tile is outside the image area, + or if it has a negative width or height (i.e. the coordinates are + swapped), bail. */ + if (tile_info.x0 >= tile_info.x1 || tile_info.y0 >= tile_info.y1 || + tile_info.x0 < 0 || tile_info.y0 < 0 || + (OPJ_UINT32)tile_info.x0 < image->x0 || + (OPJ_UINT32)tile_info.y0 < image->y0 || + (OPJ_INT32)(tile_info.x1 - image->x0) > im->xsize || + (OPJ_INT32)(tile_info.y1 - image->y0) > im->ysize) { + state->errcode = IMAGING_CODEC_BROKEN; + state->state = J2K_STATE_FAILED; + goto quick_exit; + } + + if (tile_info.nb_comps != image->numcomps) { + state->errcode = IMAGING_CODEC_BROKEN; + state->state = J2K_STATE_FAILED; + goto quick_exit; + } + + /* Sometimes the tile_info.datasize we get back from openjpeg + is less than sum(comp_bytes)*w*h, and we overflow in the + shuffle stage */ + + tile_width = tile_info.x1 - tile_info.x0; + tile_height = tile_info.y1 - tile_info.y0; + + /* Total component width = sum (component_width) e.g, it's + legal for an la file to have a 1 byte width for l, and 4 for + a. and then a malicious file could have a smaller tile_bytes + */ + + for (n=0; n < tile_info.nb_comps; n++) { + // see csize /acsize calcs + int csize = (image->comps[n].prec + 7) >> 3; + csize = (csize == 3) ? 4 : csize; + total_component_width += csize; + } + if ((tile_width > UINT_MAX / total_component_width) || + (tile_height > UINT_MAX / total_component_width) || + (tile_width > UINT_MAX / (tile_height * total_component_width)) || + (tile_height > UINT_MAX / (tile_width * total_component_width))) { + state->errcode = IMAGING_CODEC_BROKEN; + state->state = J2K_STATE_FAILED; + goto quick_exit; + } + + tile_bytes = tile_width * tile_height * total_component_width; + + if (tile_bytes > tile_info.data_size) { + tile_info.data_size = tile_bytes; + } + if (buffer_size < tile_info.data_size) { /* malloc check ok, tile_info.data_size from openjpeg */ UINT8 *new = realloc (state->buffer, tile_info.data_size);