diff options
| author | Armin Kuster <akuster@mvista.com> | 2021-09-15 17:04:16 -0700 |
|---|---|---|
| committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2021-09-30 00:02:22 +0100 |
| commit | 81bb24c0f764fb371a2822649ef92bff208a82a0 (patch) | |
| tree | 04e6d1d005f58428d7f196ac4628462382334f48 | |
| parent | 874fe76b00f7a8b31477417611df9929c21c1eaf (diff) | |
| download | poky-81bb24c0f764fb371a2822649ef92bff208a82a0.tar.gz | |
qemu: Security fix CVE-2020-12829
Source: qemu.org
MR: 105490
Type: Security Fix
Disposition: Backport from https://git.qemu.org/?p=qemu.git;a=commit;h=b15a22bbcbe6a78dc3d88fe3134985e4cdd87de4
ChangeID: 6e222b766fc67c76cdc311d02cc47801992d0e66
Description:
Affect qemu < 5.0.0
(From OE-Core rev: 7cd5c38b6d078c22519ad6b6e89caa9c1aa5ecd4)
Signed-off-by: Armin Kuster <akuster@mvista.com>
Signed-off-by: Steve Sakoman <steve@sakoman.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
| -rw-r--r-- | meta/recipes-devtools/qemu/qemu.inc | 5 | ||||
| -rw-r--r-- | meta/recipes-devtools/qemu/qemu/CVE-2020-12829_1.patch | 164 | ||||
| -rw-r--r-- | meta/recipes-devtools/qemu/qemu/CVE-2020-12829_2.patch | 139 | ||||
| -rw-r--r-- | meta/recipes-devtools/qemu/qemu/CVE-2020-12829_3.patch | 47 | ||||
| -rw-r--r-- | meta/recipes-devtools/qemu/qemu/CVE-2020-12829_4.patch | 100 | ||||
| -rw-r--r-- | meta/recipes-devtools/qemu/qemu/CVE-2020-12829_5.patch | 266 |
6 files changed, 721 insertions, 0 deletions
diff --git a/meta/recipes-devtools/qemu/qemu.inc b/meta/recipes-devtools/qemu/qemu.inc index c8c170dda0..f5e8a9ae49 100644 --- a/meta/recipes-devtools/qemu/qemu.inc +++ b/meta/recipes-devtools/qemu/qemu.inc | |||
| @@ -84,6 +84,11 @@ SRC_URI = "https://download.qemu.org/${BPN}-${PV}.tar.xz \ | |||
| 84 | file://CVE-2021-3582.patch \ | 84 | file://CVE-2021-3582.patch \ |
| 85 | file://CVE-2021-3607.patch \ | 85 | file://CVE-2021-3607.patch \ |
| 86 | file://CVE-2021-3608.patch \ | 86 | file://CVE-2021-3608.patch \ |
| 87 | file://CVE-2020-12829_1.patch \ | ||
| 88 | file://CVE-2020-12829_2.patch \ | ||
| 89 | file://CVE-2020-12829_3.patch \ | ||
| 90 | file://CVE-2020-12829_4.patch \ | ||
| 91 | file://CVE-2020-12829_5.patch \ | ||
| 87 | " | 92 | " |
| 88 | UPSTREAM_CHECK_REGEX = "qemu-(?P<pver>\d+(\.\d+)+)\.tar" | 93 | UPSTREAM_CHECK_REGEX = "qemu-(?P<pver>\d+(\.\d+)+)\.tar" |
| 89 | 94 | ||
diff --git a/meta/recipes-devtools/qemu/qemu/CVE-2020-12829_1.patch b/meta/recipes-devtools/qemu/qemu/CVE-2020-12829_1.patch new file mode 100644 index 0000000000..6fee4f640d --- /dev/null +++ b/meta/recipes-devtools/qemu/qemu/CVE-2020-12829_1.patch | |||
| @@ -0,0 +1,164 @@ | |||
| 1 | From e29da77e5fddf6480e3a0e80b63d703edaec751b Mon Sep 17 00:00:00 2001 | ||
| 2 | From: BALATON Zoltan <balaton@eik.bme.hu> | ||
| 3 | Date: Thu, 21 May 2020 21:39:44 +0200 | ||
| 4 | Subject: [PATCH] sm501: Convert printf + abort to qemu_log_mask | ||
| 5 | MIME-Version: 1.0 | ||
| 6 | Content-Type: text/plain; charset=UTF-8 | ||
| 7 | Content-Transfer-Encoding: 8bit | ||
| 8 | |||
| 9 | Some places already use qemu_log_mask() to log unimplemented features | ||
| 10 | or errors but some others have printf() then abort(). Convert these to | ||
| 11 | qemu_log_mask() and avoid aborting to prevent guests to easily cause | ||
| 12 | denial of service. | ||
| 13 | |||
| 14 | Signed-off-by: BALATON Zoltan <balaton@eik.bme.hu> | ||
| 15 | Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com> | ||
| 16 | Message-id: 305af87f59d81e92f2aaff09eb8a3603b8baa322.1590089984.git.balaton@eik.bme.hu | ||
| 17 | Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> | ||
| 18 | |||
| 19 | Upstream-Status: Backport | ||
| 20 | CVE: CVE-2020-12829 dep#1 | ||
| 21 | Signed-off-by: Armin Kuster <akuster@mvista.com> | ||
| 22 | |||
| 23 | --- | ||
| 24 | hw/display/sm501.c | 57 ++++++++++++++++++++++------------------------ | ||
| 25 | 1 file changed, 27 insertions(+), 30 deletions(-) | ||
| 26 | |||
| 27 | diff --git a/hw/display/sm501.c b/hw/display/sm501.c | ||
| 28 | index acc692531a..bd3ccfe311 100644 | ||
| 29 | --- a/hw/display/sm501.c | ||
| 30 | +++ b/hw/display/sm501.c | ||
| 31 | @@ -727,8 +727,8 @@ static void sm501_2d_operation(SM501State *s) | ||
| 32 | int fb_len = get_width(s, crt) * get_height(s, crt) * get_bpp(s, crt); | ||
| 33 | |||
| 34 | if (addressing != 0x0) { | ||
| 35 | - printf("%s: only XY addressing is supported.\n", __func__); | ||
| 36 | - abort(); | ||
| 37 | + qemu_log_mask(LOG_UNIMP, "sm501: only XY addressing is supported.\n"); | ||
| 38 | + return; | ||
| 39 | } | ||
| 40 | |||
| 41 | if (rop_mode == 0) { | ||
| 42 | @@ -754,8 +754,8 @@ static void sm501_2d_operation(SM501State *s) | ||
| 43 | |||
| 44 | if ((s->twoD_source_base & 0x08000000) || | ||
| 45 | (s->twoD_destination_base & 0x08000000)) { | ||
| 46 | - printf("%s: only local memory is supported.\n", __func__); | ||
| 47 | - abort(); | ||
| 48 | + qemu_log_mask(LOG_UNIMP, "sm501: only local memory is supported.\n"); | ||
| 49 | + return; | ||
| 50 | } | ||
| 51 | |||
| 52 | switch (operation) { | ||
| 53 | @@ -823,9 +823,9 @@ static void sm501_2d_operation(SM501State *s) | ||
| 54 | break; | ||
| 55 | |||
| 56 | default: | ||
| 57 | - printf("non-implemented SM501 2D operation. %d\n", operation); | ||
| 58 | - abort(); | ||
| 59 | - break; | ||
| 60 | + qemu_log_mask(LOG_UNIMP, "sm501: not implemented 2D operation: %d\n", | ||
| 61 | + operation); | ||
| 62 | + return; | ||
| 63 | } | ||
| 64 | |||
| 65 | if (dst_base >= get_fb_addr(s, crt) && | ||
| 66 | @@ -892,9 +892,8 @@ static uint64_t sm501_system_config_read(void *opaque, hwaddr addr, | ||
| 67 | break; | ||
| 68 | |||
| 69 | default: | ||
| 70 | - printf("sm501 system config : not implemented register read." | ||
| 71 | - " addr=%x\n", (int)addr); | ||
| 72 | - abort(); | ||
| 73 | + qemu_log_mask(LOG_UNIMP, "sm501: not implemented system config" | ||
| 74 | + "register read. addr=%" HWADDR_PRIx "\n", addr); | ||
| 75 | } | ||
| 76 | |||
| 77 | return ret; | ||
| 78 | @@ -948,15 +947,15 @@ static void sm501_system_config_write(void *opaque, hwaddr addr, | ||
| 79 | break; | ||
| 80 | case SM501_ENDIAN_CONTROL: | ||
| 81 | if (value & 0x00000001) { | ||
| 82 | - printf("sm501 system config : big endian mode not implemented.\n"); | ||
| 83 | - abort(); | ||
| 84 | + qemu_log_mask(LOG_UNIMP, "sm501: system config big endian mode not" | ||
| 85 | + " implemented.\n"); | ||
| 86 | } | ||
| 87 | break; | ||
| 88 | |||
| 89 | default: | ||
| 90 | - printf("sm501 system config : not implemented register write." | ||
| 91 | - " addr=%x, val=%x\n", (int)addr, (uint32_t)value); | ||
| 92 | - abort(); | ||
| 93 | + qemu_log_mask(LOG_UNIMP, "sm501: not implemented system config" | ||
| 94 | + "register write. addr=%" HWADDR_PRIx | ||
| 95 | + ", val=%" PRIx64 "\n", addr, value); | ||
| 96 | } | ||
| 97 | } | ||
| 98 | |||
| 99 | @@ -1207,9 +1206,8 @@ static uint64_t sm501_disp_ctrl_read(void *opaque, hwaddr addr, | ||
| 100 | break; | ||
| 101 | |||
| 102 | default: | ||
| 103 | - printf("sm501 disp ctrl : not implemented register read." | ||
| 104 | - " addr=%x\n", (int)addr); | ||
| 105 | - abort(); | ||
| 106 | + qemu_log_mask(LOG_UNIMP, "sm501: not implemented disp ctrl register " | ||
| 107 | + "read. addr=%" HWADDR_PRIx "\n", addr); | ||
| 108 | } | ||
| 109 | |||
| 110 | return ret; | ||
| 111 | @@ -1345,9 +1343,9 @@ static void sm501_disp_ctrl_write(void *opaque, hwaddr addr, | ||
| 112 | break; | ||
| 113 | |||
| 114 | default: | ||
| 115 | - printf("sm501 disp ctrl : not implemented register write." | ||
| 116 | - " addr=%x, val=%x\n", (int)addr, (unsigned)value); | ||
| 117 | - abort(); | ||
| 118 | + qemu_log_mask(LOG_UNIMP, "sm501: not implemented disp ctrl register " | ||
| 119 | + "write. addr=%" HWADDR_PRIx | ||
| 120 | + ", val=%" PRIx64 "\n", addr, value); | ||
| 121 | } | ||
| 122 | } | ||
| 123 | |||
| 124 | @@ -1433,9 +1431,8 @@ static uint64_t sm501_2d_engine_read(void *opaque, hwaddr addr, | ||
| 125 | ret = 0; /* Should return interrupt status */ | ||
| 126 | break; | ||
| 127 | default: | ||
| 128 | - printf("sm501 disp ctrl : not implemented register read." | ||
| 129 | - " addr=%x\n", (int)addr); | ||
| 130 | - abort(); | ||
| 131 | + qemu_log_mask(LOG_UNIMP, "sm501: not implemented disp ctrl register " | ||
| 132 | + "read. addr=%" HWADDR_PRIx "\n", addr); | ||
| 133 | } | ||
| 134 | |||
| 135 | return ret; | ||
| 136 | @@ -1520,9 +1517,9 @@ static void sm501_2d_engine_write(void *opaque, hwaddr addr, | ||
| 137 | /* ignored, writing 0 should clear interrupt status */ | ||
| 138 | break; | ||
| 139 | default: | ||
| 140 | - printf("sm501 2d engine : not implemented register write." | ||
| 141 | - " addr=%x, val=%x\n", (int)addr, (unsigned)value); | ||
| 142 | - abort(); | ||
| 143 | + qemu_log_mask(LOG_UNIMP, "sm501: not implemented 2d engine register " | ||
| 144 | + "write. addr=%" HWADDR_PRIx | ||
| 145 | + ", val=%" PRIx64 "\n", addr, value); | ||
| 146 | } | ||
| 147 | } | ||
| 148 | |||
| 149 | @@ -1670,9 +1667,9 @@ static void sm501_update_display(void *opaque) | ||
| 150 | draw_line = draw_line32_funcs[dst_depth_index]; | ||
| 151 | break; | ||
| 152 | default: | ||
| 153 | - printf("sm501 update display : invalid control register value.\n"); | ||
| 154 | - abort(); | ||
| 155 | - break; | ||
| 156 | + qemu_log_mask(LOG_GUEST_ERROR, "sm501: update display" | ||
| 157 | + "invalid control register value.\n"); | ||
| 158 | + return; | ||
| 159 | } | ||
| 160 | |||
| 161 | /* set up to draw hardware cursor */ | ||
| 162 | -- | ||
| 163 | 2.25.1 | ||
| 164 | |||
diff --git a/meta/recipes-devtools/qemu/qemu/CVE-2020-12829_2.patch b/meta/recipes-devtools/qemu/qemu/CVE-2020-12829_2.patch new file mode 100644 index 0000000000..e7258a43d3 --- /dev/null +++ b/meta/recipes-devtools/qemu/qemu/CVE-2020-12829_2.patch | |||
| @@ -0,0 +1,139 @@ | |||
| 1 | From 6f8183b5dc5b309378687830a25e85ea8fb860ea Mon Sep 17 00:00:00 2001 | ||
| 2 | From: BALATON Zoltan <balaton@eik.bme.hu> | ||
| 3 | Date: Thu, 21 May 2020 21:39:44 +0200 | ||
| 4 | Subject: [PATCH 2/5] sm501: Shorten long variable names in sm501_2d_operation | ||
| 5 | |||
| 6 | This increases readability and cleans up some confusing naming. | ||
| 7 | |||
| 8 | Signed-off-by: BALATON Zoltan <balaton@eik.bme.hu> | ||
| 9 | Message-id: b9b67b94c46e945252a73c77dfd117132c63c4fb.1590089984.git.balaton@eik.bme.hu | ||
| 10 | Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> | ||
| 11 | |||
| 12 | Upstream-Status: Backport | ||
| 13 | CVE: CVE-2020-12829 dep#2 | ||
| 14 | Signed-off-by: Armin Kuster <akuster@mvista.com> | ||
| 15 | |||
| 16 | --- | ||
| 17 | hw/display/sm501.c | 45 ++++++++++++++++++++++----------------------- | ||
| 18 | 1 file changed, 22 insertions(+), 23 deletions(-) | ||
| 19 | |||
| 20 | diff --git a/hw/display/sm501.c b/hw/display/sm501.c | ||
| 21 | index bd3ccfe311..f42d05e1e4 100644 | ||
| 22 | --- a/hw/display/sm501.c | ||
| 23 | +++ b/hw/display/sm501.c | ||
| 24 | @@ -700,17 +700,16 @@ static inline void hwc_invalidate(SM501State *s, int crt) | ||
| 25 | static void sm501_2d_operation(SM501State *s) | ||
| 26 | { | ||
| 27 | /* obtain operation parameters */ | ||
| 28 | - int operation = (s->twoD_control >> 16) & 0x1f; | ||
| 29 | + int cmd = (s->twoD_control >> 16) & 0x1F; | ||
| 30 | int rtl = s->twoD_control & 0x8000000; | ||
| 31 | int src_x = (s->twoD_source >> 16) & 0x01FFF; | ||
| 32 | int src_y = s->twoD_source & 0xFFFF; | ||
| 33 | int dst_x = (s->twoD_destination >> 16) & 0x01FFF; | ||
| 34 | int dst_y = s->twoD_destination & 0xFFFF; | ||
| 35 | - int operation_width = (s->twoD_dimension >> 16) & 0x1FFF; | ||
| 36 | - int operation_height = s->twoD_dimension & 0xFFFF; | ||
| 37 | + int width = (s->twoD_dimension >> 16) & 0x1FFF; | ||
| 38 | + int height = s->twoD_dimension & 0xFFFF; | ||
| 39 | uint32_t color = s->twoD_foreground; | ||
| 40 | - int format_flags = (s->twoD_stretch >> 20) & 0x3; | ||
| 41 | - int addressing = (s->twoD_stretch >> 16) & 0xF; | ||
| 42 | + int format = (s->twoD_stretch >> 20) & 0x3; | ||
| 43 | int rop_mode = (s->twoD_control >> 15) & 0x1; /* 1 for rop2, else rop3 */ | ||
| 44 | /* 1 if rop2 source is the pattern, otherwise the source is the bitmap */ | ||
| 45 | int rop2_source_is_pattern = (s->twoD_control >> 14) & 0x1; | ||
| 46 | @@ -721,12 +720,12 @@ static void sm501_2d_operation(SM501State *s) | ||
| 47 | /* get frame buffer info */ | ||
| 48 | uint8_t *src = s->local_mem + src_base; | ||
| 49 | uint8_t *dst = s->local_mem + dst_base; | ||
| 50 | - int src_width = s->twoD_pitch & 0x1FFF; | ||
| 51 | - int dst_width = (s->twoD_pitch >> 16) & 0x1FFF; | ||
| 52 | + int src_pitch = s->twoD_pitch & 0x1FFF; | ||
| 53 | + int dst_pitch = (s->twoD_pitch >> 16) & 0x1FFF; | ||
| 54 | int crt = (s->dc_crt_control & SM501_DC_CRT_CONTROL_SEL) ? 1 : 0; | ||
| 55 | int fb_len = get_width(s, crt) * get_height(s, crt) * get_bpp(s, crt); | ||
| 56 | |||
| 57 | - if (addressing != 0x0) { | ||
| 58 | + if ((s->twoD_stretch >> 16) & 0xF) { | ||
| 59 | qemu_log_mask(LOG_UNIMP, "sm501: only XY addressing is supported.\n"); | ||
| 60 | return; | ||
| 61 | } | ||
| 62 | @@ -758,20 +757,20 @@ static void sm501_2d_operation(SM501State *s) | ||
| 63 | return; | ||
| 64 | } | ||
| 65 | |||
| 66 | - switch (operation) { | ||
| 67 | + switch (cmd) { | ||
| 68 | case 0x00: /* copy area */ | ||
| 69 | #define COPY_AREA(_bpp, _pixel_type, rtl) { \ | ||
| 70 | int y, x, index_d, index_s; \ | ||
| 71 | - for (y = 0; y < operation_height; y++) { \ | ||
| 72 | - for (x = 0; x < operation_width; x++) { \ | ||
| 73 | + for (y = 0; y < height; y++) { \ | ||
| 74 | + for (x = 0; x < width; x++) { \ | ||
| 75 | _pixel_type val; \ | ||
| 76 | \ | ||
| 77 | if (rtl) { \ | ||
| 78 | - index_s = ((src_y - y) * src_width + src_x - x) * _bpp; \ | ||
| 79 | - index_d = ((dst_y - y) * dst_width + dst_x - x) * _bpp; \ | ||
| 80 | + index_s = ((src_y - y) * src_pitch + src_x - x) * _bpp; \ | ||
| 81 | + index_d = ((dst_y - y) * dst_pitch + dst_x - x) * _bpp; \ | ||
| 82 | } else { \ | ||
| 83 | - index_s = ((src_y + y) * src_width + src_x + x) * _bpp; \ | ||
| 84 | - index_d = ((dst_y + y) * dst_width + dst_x + x) * _bpp; \ | ||
| 85 | + index_s = ((src_y + y) * src_pitch + src_x + x) * _bpp; \ | ||
| 86 | + index_d = ((dst_y + y) * dst_pitch + dst_x + x) * _bpp; \ | ||
| 87 | } \ | ||
| 88 | if (rop_mode == 1 && rop == 5) { \ | ||
| 89 | /* Invert dest */ \ | ||
| 90 | @@ -783,7 +782,7 @@ static void sm501_2d_operation(SM501State *s) | ||
| 91 | } \ | ||
| 92 | } \ | ||
| 93 | } | ||
| 94 | - switch (format_flags) { | ||
| 95 | + switch (format) { | ||
| 96 | case 0: | ||
| 97 | COPY_AREA(1, uint8_t, rtl); | ||
| 98 | break; | ||
| 99 | @@ -799,15 +798,15 @@ static void sm501_2d_operation(SM501State *s) | ||
| 100 | case 0x01: /* fill rectangle */ | ||
| 101 | #define FILL_RECT(_bpp, _pixel_type) { \ | ||
| 102 | int y, x; \ | ||
| 103 | - for (y = 0; y < operation_height; y++) { \ | ||
| 104 | - for (x = 0; x < operation_width; x++) { \ | ||
| 105 | - int index = ((dst_y + y) * dst_width + dst_x + x) * _bpp; \ | ||
| 106 | + for (y = 0; y < height; y++) { \ | ||
| 107 | + for (x = 0; x < width; x++) { \ | ||
| 108 | + int index = ((dst_y + y) * dst_pitch + dst_x + x) * _bpp; \ | ||
| 109 | *(_pixel_type *)&dst[index] = (_pixel_type)color; \ | ||
| 110 | } \ | ||
| 111 | } \ | ||
| 112 | } | ||
| 113 | |||
| 114 | - switch (format_flags) { | ||
| 115 | + switch (format) { | ||
| 116 | case 0: | ||
| 117 | FILL_RECT(1, uint8_t); | ||
| 118 | break; | ||
| 119 | @@ -824,14 +823,14 @@ static void sm501_2d_operation(SM501State *s) | ||
| 120 | |||
| 121 | default: | ||
| 122 | qemu_log_mask(LOG_UNIMP, "sm501: not implemented 2D operation: %d\n", | ||
| 123 | - operation); | ||
| 124 | + cmd); | ||
| 125 | return; | ||
| 126 | } | ||
| 127 | |||
| 128 | if (dst_base >= get_fb_addr(s, crt) && | ||
| 129 | dst_base <= get_fb_addr(s, crt) + fb_len) { | ||
| 130 | - int dst_len = MIN(fb_len, ((dst_y + operation_height - 1) * dst_width + | ||
| 131 | - dst_x + operation_width) * (1 << format_flags)); | ||
| 132 | + int dst_len = MIN(fb_len, ((dst_y + height - 1) * dst_pitch + | ||
| 133 | + dst_x + width) * (1 << format)); | ||
| 134 | if (dst_len) { | ||
| 135 | memory_region_set_dirty(&s->local_mem_region, dst_base, dst_len); | ||
| 136 | } | ||
| 137 | -- | ||
| 138 | 2.25.1 | ||
| 139 | |||
diff --git a/meta/recipes-devtools/qemu/qemu/CVE-2020-12829_3.patch b/meta/recipes-devtools/qemu/qemu/CVE-2020-12829_3.patch new file mode 100644 index 0000000000..c647028cfe --- /dev/null +++ b/meta/recipes-devtools/qemu/qemu/CVE-2020-12829_3.patch | |||
| @@ -0,0 +1,47 @@ | |||
| 1 | From 2824809b7f8f03ddc6e2b7e33e78c06022424298 Mon Sep 17 00:00:00 2001 | ||
| 2 | From: BALATON Zoltan <balaton@eik.bme.hu> | ||
| 3 | Date: Thu, 21 May 2020 21:39:44 +0200 | ||
| 4 | Subject: [PATCH 3/5] sm501: Use BIT(x) macro to shorten constant | ||
| 5 | MIME-Version: 1.0 | ||
| 6 | Content-Type: text/plain; charset=UTF-8 | ||
| 7 | Content-Transfer-Encoding: 8bit | ||
| 8 | |||
| 9 | Signed-off-by: BALATON Zoltan <balaton@eik.bme.hu> | ||
| 10 | Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com> | ||
| 11 | Message-id: 124bf5de8d7cf503b32b377d0445029a76bfbd49.1590089984.git.balaton@eik.bme.hu | ||
| 12 | Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> | ||
| 13 | |||
| 14 | Upstream-Status: Backport | ||
| 15 | CVE: CVE-2020-12829 dep#3 | ||
| 16 | Signed-off-by: Armin Kuster <akuster@mvista.com> | ||
| 17 | |||
| 18 | --- | ||
| 19 | hw/display/sm501.c | 5 ++--- | ||
| 20 | 1 file changed, 2 insertions(+), 3 deletions(-) | ||
| 21 | |||
| 22 | diff --git a/hw/display/sm501.c b/hw/display/sm501.c | ||
| 23 | index f42d05e1e4..97660090bb 100644 | ||
| 24 | --- a/hw/display/sm501.c | ||
| 25 | +++ b/hw/display/sm501.c | ||
| 26 | @@ -701,7 +701,7 @@ static void sm501_2d_operation(SM501State *s) | ||
| 27 | { | ||
| 28 | /* obtain operation parameters */ | ||
| 29 | int cmd = (s->twoD_control >> 16) & 0x1F; | ||
| 30 | - int rtl = s->twoD_control & 0x8000000; | ||
| 31 | + int rtl = s->twoD_control & BIT(27); | ||
| 32 | int src_x = (s->twoD_source >> 16) & 0x01FFF; | ||
| 33 | int src_y = s->twoD_source & 0xFFFF; | ||
| 34 | int dst_x = (s->twoD_destination >> 16) & 0x01FFF; | ||
| 35 | @@ -751,8 +751,7 @@ static void sm501_2d_operation(SM501State *s) | ||
| 36 | } | ||
| 37 | } | ||
| 38 | |||
| 39 | - if ((s->twoD_source_base & 0x08000000) || | ||
| 40 | - (s->twoD_destination_base & 0x08000000)) { | ||
| 41 | + if (s->twoD_source_base & BIT(27) || s->twoD_destination_base & BIT(27)) { | ||
| 42 | qemu_log_mask(LOG_UNIMP, "sm501: only local memory is supported.\n"); | ||
| 43 | return; | ||
| 44 | } | ||
| 45 | -- | ||
| 46 | 2.25.1 | ||
| 47 | |||
diff --git a/meta/recipes-devtools/qemu/qemu/CVE-2020-12829_4.patch b/meta/recipes-devtools/qemu/qemu/CVE-2020-12829_4.patch new file mode 100644 index 0000000000..485af05e1e --- /dev/null +++ b/meta/recipes-devtools/qemu/qemu/CVE-2020-12829_4.patch | |||
| @@ -0,0 +1,100 @@ | |||
| 1 | From 3d0b096298b5579a7fa0753ad90968b27bc65372 Mon Sep 17 00:00:00 2001 | ||
| 2 | From: BALATON Zoltan <balaton@eik.bme.hu> | ||
| 3 | Date: Thu, 21 May 2020 21:39:44 +0200 | ||
| 4 | Subject: [PATCH 4/5] sm501: Clean up local variables in sm501_2d_operation | ||
| 5 | MIME-Version: 1.0 | ||
| 6 | Content-Type: text/plain; charset=UTF-8 | ||
| 7 | Content-Transfer-Encoding: 8bit | ||
| 8 | |||
| 9 | Make variables local to the block they are used in to make it clearer | ||
| 10 | which operation they are needed for. | ||
| 11 | |||
| 12 | Signed-off-by: BALATON Zoltan <balaton@eik.bme.hu> | ||
| 13 | Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com> | ||
| 14 | Message-id: ae59f8138afe7f6a5a4a82539d0f61496a906b06.1590089984.git.balaton@eik.bme.hu | ||
| 15 | Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> | ||
| 16 | |||
| 17 | Upstream-Status: Backport | ||
| 18 | CVE: CVE-2020-12829 dep#4 | ||
| 19 | Signed-off-by: Armin Kuster <akuster@mvista.com> | ||
| 20 | |||
| 21 | --- | ||
| 22 | hw/display/sm501.c | 31 ++++++++++++++++--------------- | ||
| 23 | 1 file changed, 16 insertions(+), 15 deletions(-) | ||
| 24 | |||
| 25 | diff --git a/hw/display/sm501.c b/hw/display/sm501.c | ||
| 26 | index 97660090bb..5ed57703d8 100644 | ||
| 27 | --- a/hw/display/sm501.c | ||
| 28 | +++ b/hw/display/sm501.c | ||
| 29 | @@ -699,28 +699,19 @@ static inline void hwc_invalidate(SM501State *s, int crt) | ||
| 30 | |||
| 31 | static void sm501_2d_operation(SM501State *s) | ||
| 32 | { | ||
| 33 | - /* obtain operation parameters */ | ||
| 34 | int cmd = (s->twoD_control >> 16) & 0x1F; | ||
| 35 | int rtl = s->twoD_control & BIT(27); | ||
| 36 | - int src_x = (s->twoD_source >> 16) & 0x01FFF; | ||
| 37 | - int src_y = s->twoD_source & 0xFFFF; | ||
| 38 | - int dst_x = (s->twoD_destination >> 16) & 0x01FFF; | ||
| 39 | - int dst_y = s->twoD_destination & 0xFFFF; | ||
| 40 | - int width = (s->twoD_dimension >> 16) & 0x1FFF; | ||
| 41 | - int height = s->twoD_dimension & 0xFFFF; | ||
| 42 | - uint32_t color = s->twoD_foreground; | ||
| 43 | int format = (s->twoD_stretch >> 20) & 0x3; | ||
| 44 | int rop_mode = (s->twoD_control >> 15) & 0x1; /* 1 for rop2, else rop3 */ | ||
| 45 | /* 1 if rop2 source is the pattern, otherwise the source is the bitmap */ | ||
| 46 | int rop2_source_is_pattern = (s->twoD_control >> 14) & 0x1; | ||
| 47 | int rop = s->twoD_control & 0xFF; | ||
| 48 | - uint32_t src_base = s->twoD_source_base & 0x03FFFFFF; | ||
| 49 | + int dst_x = (s->twoD_destination >> 16) & 0x01FFF; | ||
| 50 | + int dst_y = s->twoD_destination & 0xFFFF; | ||
| 51 | + int width = (s->twoD_dimension >> 16) & 0x1FFF; | ||
| 52 | + int height = s->twoD_dimension & 0xFFFF; | ||
| 53 | uint32_t dst_base = s->twoD_destination_base & 0x03FFFFFF; | ||
| 54 | - | ||
| 55 | - /* get frame buffer info */ | ||
| 56 | - uint8_t *src = s->local_mem + src_base; | ||
| 57 | uint8_t *dst = s->local_mem + dst_base; | ||
| 58 | - int src_pitch = s->twoD_pitch & 0x1FFF; | ||
| 59 | int dst_pitch = (s->twoD_pitch >> 16) & 0x1FFF; | ||
| 60 | int crt = (s->dc_crt_control & SM501_DC_CRT_CONTROL_SEL) ? 1 : 0; | ||
| 61 | int fb_len = get_width(s, crt) * get_height(s, crt) * get_bpp(s, crt); | ||
| 62 | @@ -758,6 +749,13 @@ static void sm501_2d_operation(SM501State *s) | ||
| 63 | |||
| 64 | switch (cmd) { | ||
| 65 | case 0x00: /* copy area */ | ||
| 66 | + { | ||
| 67 | + int src_x = (s->twoD_source >> 16) & 0x01FFF; | ||
| 68 | + int src_y = s->twoD_source & 0xFFFF; | ||
| 69 | + uint32_t src_base = s->twoD_source_base & 0x03FFFFFF; | ||
| 70 | + uint8_t *src = s->local_mem + src_base; | ||
| 71 | + int src_pitch = s->twoD_pitch & 0x1FFF; | ||
| 72 | + | ||
| 73 | #define COPY_AREA(_bpp, _pixel_type, rtl) { \ | ||
| 74 | int y, x, index_d, index_s; \ | ||
| 75 | for (y = 0; y < height; y++) { \ | ||
| 76 | @@ -793,8 +791,11 @@ static void sm501_2d_operation(SM501State *s) | ||
| 77 | break; | ||
| 78 | } | ||
| 79 | break; | ||
| 80 | - | ||
| 81 | + } | ||
| 82 | case 0x01: /* fill rectangle */ | ||
| 83 | + { | ||
| 84 | + uint32_t color = s->twoD_foreground; | ||
| 85 | + | ||
| 86 | #define FILL_RECT(_bpp, _pixel_type) { \ | ||
| 87 | int y, x; \ | ||
| 88 | for (y = 0; y < height; y++) { \ | ||
| 89 | @@ -819,7 +820,7 @@ static void sm501_2d_operation(SM501State *s) | ||
| 90 | break; | ||
| 91 | } | ||
| 92 | break; | ||
| 93 | - | ||
| 94 | + } | ||
| 95 | default: | ||
| 96 | qemu_log_mask(LOG_UNIMP, "sm501: not implemented 2D operation: %d\n", | ||
| 97 | cmd); | ||
| 98 | -- | ||
| 99 | 2.25.1 | ||
| 100 | |||
diff --git a/meta/recipes-devtools/qemu/qemu/CVE-2020-12829_5.patch b/meta/recipes-devtools/qemu/qemu/CVE-2020-12829_5.patch new file mode 100644 index 0000000000..ab09e8b039 --- /dev/null +++ b/meta/recipes-devtools/qemu/qemu/CVE-2020-12829_5.patch | |||
| @@ -0,0 +1,266 @@ | |||
| 1 | From b15a22bbcbe6a78dc3d88fe3134985e4cdd87de4 Mon Sep 17 00:00:00 2001 | ||
| 2 | From: BALATON Zoltan <balaton@eik.bme.hu> | ||
| 3 | Date: Thu, 21 May 2020 21:39:44 +0200 | ||
| 4 | Subject: [PATCH 5/5] sm501: Replace hand written implementation with pixman | ||
| 5 | where possible | ||
| 6 | |||
| 7 | Besides being faster this should also prevent malicious guests to | ||
| 8 | abuse 2D engine to overwrite data or cause a crash. | ||
| 9 | |||
| 10 | Signed-off-by: BALATON Zoltan <balaton@eik.bme.hu> | ||
| 11 | Message-id: 58666389b6cae256e4e972a32c05cf8aa51bffc0.1590089984.git.balaton@eik.bme.hu | ||
| 12 | Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> | ||
| 13 | |||
| 14 | Upstream-Status: Backport | ||
| 15 | CVE: CVE-2020-12829 | ||
| 16 | Signed-off-by: Armin Kuster <akuster@mvista.com> | ||
| 17 | |||
| 18 | --- | ||
| 19 | hw/display/sm501.c | 207 ++++++++++++++++++++++++++------------------- | ||
| 20 | 1 file changed, 119 insertions(+), 88 deletions(-) | ||
| 21 | |||
| 22 | diff --git a/hw/display/sm501.c b/hw/display/sm501.c | ||
| 23 | index 5ed57703d8..8bf4d111f4 100644 | ||
| 24 | --- a/hw/display/sm501.c | ||
| 25 | +++ b/hw/display/sm501.c | ||
| 26 | @@ -706,13 +706,12 @@ static void sm501_2d_operation(SM501State *s) | ||
| 27 | /* 1 if rop2 source is the pattern, otherwise the source is the bitmap */ | ||
| 28 | int rop2_source_is_pattern = (s->twoD_control >> 14) & 0x1; | ||
| 29 | int rop = s->twoD_control & 0xFF; | ||
| 30 | - int dst_x = (s->twoD_destination >> 16) & 0x01FFF; | ||
| 31 | - int dst_y = s->twoD_destination & 0xFFFF; | ||
| 32 | - int width = (s->twoD_dimension >> 16) & 0x1FFF; | ||
| 33 | - int height = s->twoD_dimension & 0xFFFF; | ||
| 34 | + unsigned int dst_x = (s->twoD_destination >> 16) & 0x01FFF; | ||
| 35 | + unsigned int dst_y = s->twoD_destination & 0xFFFF; | ||
| 36 | + unsigned int width = (s->twoD_dimension >> 16) & 0x1FFF; | ||
| 37 | + unsigned int height = s->twoD_dimension & 0xFFFF; | ||
| 38 | uint32_t dst_base = s->twoD_destination_base & 0x03FFFFFF; | ||
| 39 | - uint8_t *dst = s->local_mem + dst_base; | ||
| 40 | - int dst_pitch = (s->twoD_pitch >> 16) & 0x1FFF; | ||
| 41 | + unsigned int dst_pitch = (s->twoD_pitch >> 16) & 0x1FFF; | ||
| 42 | int crt = (s->dc_crt_control & SM501_DC_CRT_CONTROL_SEL) ? 1 : 0; | ||
| 43 | int fb_len = get_width(s, crt) * get_height(s, crt) * get_bpp(s, crt); | ||
| 44 | |||
| 45 | @@ -721,104 +720,136 @@ static void sm501_2d_operation(SM501State *s) | ||
| 46 | return; | ||
| 47 | } | ||
| 48 | |||
| 49 | - if (rop_mode == 0) { | ||
| 50 | - if (rop != 0xcc) { | ||
| 51 | - /* Anything other than plain copies are not supported */ | ||
| 52 | - qemu_log_mask(LOG_UNIMP, "sm501: rop3 mode with rop %x is not " | ||
| 53 | - "supported.\n", rop); | ||
| 54 | - } | ||
| 55 | - } else { | ||
| 56 | - if (rop2_source_is_pattern && rop != 0x5) { | ||
| 57 | - /* For pattern source, we support only inverse dest */ | ||
| 58 | - qemu_log_mask(LOG_UNIMP, "sm501: rop2 source being the pattern and " | ||
| 59 | - "rop %x is not supported.\n", rop); | ||
| 60 | - } else { | ||
| 61 | - if (rop != 0x5 && rop != 0xc) { | ||
| 62 | - /* Anything other than plain copies or inverse dest is not | ||
| 63 | - * supported */ | ||
| 64 | - qemu_log_mask(LOG_UNIMP, "sm501: rop mode %x is not " | ||
| 65 | - "supported.\n", rop); | ||
| 66 | - } | ||
| 67 | - } | ||
| 68 | - } | ||
| 69 | - | ||
| 70 | if (s->twoD_source_base & BIT(27) || s->twoD_destination_base & BIT(27)) { | ||
| 71 | qemu_log_mask(LOG_UNIMP, "sm501: only local memory is supported.\n"); | ||
| 72 | return; | ||
| 73 | } | ||
| 74 | |||
| 75 | + if (!dst_pitch) { | ||
| 76 | + qemu_log_mask(LOG_GUEST_ERROR, "sm501: Zero dest pitch.\n"); | ||
| 77 | + return; | ||
| 78 | + } | ||
| 79 | + | ||
| 80 | + if (!width || !height) { | ||
| 81 | + qemu_log_mask(LOG_GUEST_ERROR, "sm501: Zero size 2D op.\n"); | ||
| 82 | + return; | ||
| 83 | + } | ||
| 84 | + | ||
| 85 | + if (rtl) { | ||
| 86 | + dst_x -= width - 1; | ||
| 87 | + dst_y -= height - 1; | ||
| 88 | + } | ||
| 89 | + | ||
| 90 | + if (dst_base >= get_local_mem_size(s) || dst_base + | ||
| 91 | + (dst_x + width + (dst_y + height) * (dst_pitch + width)) * | ||
| 92 | + (1 << format) >= get_local_mem_size(s)) { | ||
| 93 | + qemu_log_mask(LOG_GUEST_ERROR, "sm501: 2D op dest is outside vram.\n"); | ||
| 94 | + return; | ||
| 95 | + } | ||
| 96 | + | ||
| 97 | switch (cmd) { | ||
| 98 | - case 0x00: /* copy area */ | ||
| 99 | + case 0: /* BitBlt */ | ||
| 100 | { | ||
| 101 | - int src_x = (s->twoD_source >> 16) & 0x01FFF; | ||
| 102 | - int src_y = s->twoD_source & 0xFFFF; | ||
| 103 | + unsigned int src_x = (s->twoD_source >> 16) & 0x01FFF; | ||
| 104 | + unsigned int src_y = s->twoD_source & 0xFFFF; | ||
| 105 | uint32_t src_base = s->twoD_source_base & 0x03FFFFFF; | ||
| 106 | - uint8_t *src = s->local_mem + src_base; | ||
| 107 | - int src_pitch = s->twoD_pitch & 0x1FFF; | ||
| 108 | - | ||
| 109 | -#define COPY_AREA(_bpp, _pixel_type, rtl) { \ | ||
| 110 | - int y, x, index_d, index_s; \ | ||
| 111 | - for (y = 0; y < height; y++) { \ | ||
| 112 | - for (x = 0; x < width; x++) { \ | ||
| 113 | - _pixel_type val; \ | ||
| 114 | - \ | ||
| 115 | - if (rtl) { \ | ||
| 116 | - index_s = ((src_y - y) * src_pitch + src_x - x) * _bpp; \ | ||
| 117 | - index_d = ((dst_y - y) * dst_pitch + dst_x - x) * _bpp; \ | ||
| 118 | - } else { \ | ||
| 119 | - index_s = ((src_y + y) * src_pitch + src_x + x) * _bpp; \ | ||
| 120 | - index_d = ((dst_y + y) * dst_pitch + dst_x + x) * _bpp; \ | ||
| 121 | - } \ | ||
| 122 | - if (rop_mode == 1 && rop == 5) { \ | ||
| 123 | - /* Invert dest */ \ | ||
| 124 | - val = ~*(_pixel_type *)&dst[index_d]; \ | ||
| 125 | - } else { \ | ||
| 126 | - val = *(_pixel_type *)&src[index_s]; \ | ||
| 127 | - } \ | ||
| 128 | - *(_pixel_type *)&dst[index_d] = val; \ | ||
| 129 | - } \ | ||
| 130 | - } \ | ||
| 131 | - } | ||
| 132 | - switch (format) { | ||
| 133 | - case 0: | ||
| 134 | - COPY_AREA(1, uint8_t, rtl); | ||
| 135 | - break; | ||
| 136 | - case 1: | ||
| 137 | - COPY_AREA(2, uint16_t, rtl); | ||
| 138 | - break; | ||
| 139 | - case 2: | ||
| 140 | - COPY_AREA(4, uint32_t, rtl); | ||
| 141 | - break; | ||
| 142 | + unsigned int src_pitch = s->twoD_pitch & 0x1FFF; | ||
| 143 | + | ||
| 144 | + if (!src_pitch) { | ||
| 145 | + qemu_log_mask(LOG_GUEST_ERROR, "sm501: Zero src pitch.\n"); | ||
| 146 | + return; | ||
| 147 | + } | ||
| 148 | + | ||
| 149 | + if (rtl) { | ||
| 150 | + src_x -= width - 1; | ||
| 151 | + src_y -= height - 1; | ||
| 152 | + } | ||
| 153 | + | ||
| 154 | + if (src_base >= get_local_mem_size(s) || src_base + | ||
| 155 | + (src_x + width + (src_y + height) * (src_pitch + width)) * | ||
| 156 | + (1 << format) >= get_local_mem_size(s)) { | ||
| 157 | + qemu_log_mask(LOG_GUEST_ERROR, | ||
| 158 | + "sm501: 2D op src is outside vram.\n"); | ||
| 159 | + return; | ||
| 160 | + } | ||
| 161 | + | ||
| 162 | + if ((rop_mode && rop == 0x5) || (!rop_mode && rop == 0x55)) { | ||
| 163 | + /* Invert dest, is there a way to do this with pixman? */ | ||
| 164 | + unsigned int x, y, i; | ||
| 165 | + uint8_t *d = s->local_mem + dst_base; | ||
| 166 | + | ||
| 167 | + for (y = 0; y < height; y++) { | ||
| 168 | + i = (dst_x + (dst_y + y) * dst_pitch) * (1 << format); | ||
| 169 | + for (x = 0; x < width; x++, i += (1 << format)) { | ||
| 170 | + switch (format) { | ||
| 171 | + case 0: | ||
| 172 | + d[i] = ~d[i]; | ||
| 173 | + break; | ||
| 174 | + case 1: | ||
| 175 | + *(uint16_t *)&d[i] = ~*(uint16_t *)&d[i]; | ||
| 176 | + break; | ||
| 177 | + case 2: | ||
| 178 | + *(uint32_t *)&d[i] = ~*(uint32_t *)&d[i]; | ||
| 179 | + break; | ||
| 180 | + } | ||
| 181 | + } | ||
| 182 | + } | ||
| 183 | + } else { | ||
| 184 | + /* Do copy src for unimplemented ops, better than unpainted area */ | ||
| 185 | + if ((rop_mode && (rop != 0xc || rop2_source_is_pattern)) || | ||
| 186 | + (!rop_mode && rop != 0xcc)) { | ||
| 187 | + qemu_log_mask(LOG_UNIMP, | ||
| 188 | + "sm501: rop%d op %x%s not implemented\n", | ||
| 189 | + (rop_mode ? 2 : 3), rop, | ||
| 190 | + (rop2_source_is_pattern ? | ||
| 191 | + " with pattern source" : "")); | ||
| 192 | + } | ||
| 193 | + /* Check for overlaps, this could be made more exact */ | ||
| 194 | + uint32_t sb, se, db, de; | ||
| 195 | + sb = src_base + src_x + src_y * (width + src_pitch); | ||
| 196 | + se = sb + width + height * (width + src_pitch); | ||
| 197 | + db = dst_base + dst_x + dst_y * (width + dst_pitch); | ||
| 198 | + de = db + width + height * (width + dst_pitch); | ||
| 199 | + if (rtl && ((db >= sb && db <= se) || (de >= sb && de <= se))) { | ||
| 200 | + /* regions may overlap: copy via temporary */ | ||
| 201 | + int llb = width * (1 << format); | ||
| 202 | + int tmp_stride = DIV_ROUND_UP(llb, sizeof(uint32_t)); | ||
| 203 | + uint32_t *tmp = g_malloc(tmp_stride * sizeof(uint32_t) * | ||
| 204 | + height); | ||
| 205 | + pixman_blt((uint32_t *)&s->local_mem[src_base], tmp, | ||
| 206 | + src_pitch * (1 << format) / sizeof(uint32_t), | ||
| 207 | + tmp_stride, 8 * (1 << format), 8 * (1 << format), | ||
| 208 | + src_x, src_y, 0, 0, width, height); | ||
| 209 | + pixman_blt(tmp, (uint32_t *)&s->local_mem[dst_base], | ||
| 210 | + tmp_stride, | ||
| 211 | + dst_pitch * (1 << format) / sizeof(uint32_t), | ||
| 212 | + 8 * (1 << format), 8 * (1 << format), | ||
| 213 | + 0, 0, dst_x, dst_y, width, height); | ||
| 214 | + g_free(tmp); | ||
| 215 | + } else { | ||
| 216 | + pixman_blt((uint32_t *)&s->local_mem[src_base], | ||
| 217 | + (uint32_t *)&s->local_mem[dst_base], | ||
| 218 | + src_pitch * (1 << format) / sizeof(uint32_t), | ||
| 219 | + dst_pitch * (1 << format) / sizeof(uint32_t), | ||
| 220 | + 8 * (1 << format), 8 * (1 << format), | ||
| 221 | + src_x, src_y, dst_x, dst_y, width, height); | ||
| 222 | + } | ||
| 223 | } | ||
| 224 | break; | ||
| 225 | } | ||
| 226 | - case 0x01: /* fill rectangle */ | ||
| 227 | + case 1: /* Rectangle Fill */ | ||
| 228 | { | ||
| 229 | uint32_t color = s->twoD_foreground; | ||
| 230 | |||
| 231 | -#define FILL_RECT(_bpp, _pixel_type) { \ | ||
| 232 | - int y, x; \ | ||
| 233 | - for (y = 0; y < height; y++) { \ | ||
| 234 | - for (x = 0; x < width; x++) { \ | ||
| 235 | - int index = ((dst_y + y) * dst_pitch + dst_x + x) * _bpp; \ | ||
| 236 | - *(_pixel_type *)&dst[index] = (_pixel_type)color; \ | ||
| 237 | - } \ | ||
| 238 | - } \ | ||
| 239 | - } | ||
| 240 | - | ||
| 241 | - switch (format) { | ||
| 242 | - case 0: | ||
| 243 | - FILL_RECT(1, uint8_t); | ||
| 244 | - break; | ||
| 245 | - case 1: | ||
| 246 | - color = cpu_to_le16(color); | ||
| 247 | - FILL_RECT(2, uint16_t); | ||
| 248 | - break; | ||
| 249 | - case 2: | ||
| 250 | + if (format == 2) { | ||
| 251 | color = cpu_to_le32(color); | ||
| 252 | - FILL_RECT(4, uint32_t); | ||
| 253 | - break; | ||
| 254 | + } else if (format == 1) { | ||
| 255 | + color = cpu_to_le16(color); | ||
| 256 | } | ||
| 257 | + | ||
| 258 | + pixman_fill((uint32_t *)&s->local_mem[dst_base], | ||
| 259 | + dst_pitch * (1 << format) / sizeof(uint32_t), | ||
| 260 | + 8 * (1 << format), dst_x, dst_y, width, height, color); | ||
| 261 | break; | ||
| 262 | } | ||
| 263 | default: | ||
| 264 | -- | ||
| 265 | 2.25.1 | ||
| 266 | |||
