Spo*_*rth 8 c integer-overflow libexif
请原谅我这个问题.我寻找类似问题的答案,但我仍然对我的问题感到困惑.所以无论如何我会拍这个问题.我正在使用名为libexif的C库来处理图像数据.我在我的Linux桌面和MIPS板上运行我的应用程序(使用此库).对于特定的图像文件,当我尝试获取创建的时间时,我收到错误/无效值.在进一步调试时,我看到对于这个特定的图像文件,我没有按预期获得标记(EXIF_TAG_DATE_TIME).
该库具有多个实用功能.大多数功能的结构如下
int16_t
exif_get_sshort (const unsigned char *buf, ExifByteOrder order)
{
if (!buf) return 0;
switch (order) {
case EXIF_BYTE_ORDER_MOTOROLA:
return ((buf[0] << 8) | buf[1]);
case EXIF_BYTE_ORDER_INTEL:
return ((buf[1] << 8) | buf[0]);
}
/* Won't be reached */
return (0);
}
uint16_t
exif_get_short (const unsigned char *buf, ExifByteOrder order)
{
return (exif_get_sshort (buf, order) & 0xffff);
}
Run Code Online (Sandbox Code Playgroud)
当库试图调查原始数据中是否存在标记时,它会调用exif_get_short()并将返回的值赋给一个类型为enum(int)的变量.
在错误情况下,exif_get_short()应该返回无符号值(34687)返回一个负数(-30871),这会从图像数据中提取整个标记提取.
34687超出了最大可表示的int16_t值的范围.因此导致溢出.当我在代码中进行这种轻微修改时,一切似乎都运行正常
uint16_t
exif_get_short (const unsigned char *buf, ExifByteOrder order)
{
int temp = (exif_get_sshort (buf, order) & 0xffff);
return temp;
}
Run Code Online (Sandbox Code Playgroud)
但由于这是一个非常稳定的库并且使用了相当长的一段时间,它让我相信我可能会在这里遗漏一些东西.此外,这是为其他实用程序功能构建代码的一般方式.例如:exif_get_long()打电话exif_get_slong().然后我必须更改所有实用程序功能.
令我困惑的是,当我在我的linux桌面上运行这段代码以获取错误文件时,我发现没有任何问题,并且原始库代码的工作正常.这让我相信UINT16_MAX和INT16_MAX宏可能在我的桌面和MIPS板上有不同的值.但不幸的是,情况并非如此.两者都在电路板和桌面上打印相同的值.如果这段代码失败,它也应该在我的桌面上失败.
我在这里错过了什么?任何提示都将非常感激.
编辑:调用exif_get_short()的代码是这样的:
ExifTag tag;
...
tag = exif_get_short (d + offset + 12 * i, data->priv->order);
switch (tag) {
...
...
Run Code Online (Sandbox Code Playgroud)
ExifTag类型如下:
typedef enum {
EXIF_TAG_GPS_VERSION_ID = 0x0000,
EXIF_TAG_INTEROPERABILITY_INDEX = 0x0001,
...
...
}ExifTag ;
Run Code Online (Sandbox Code Playgroud)
使用的交叉编译器是mipsisa32r2el-timesys-linux-gnu-gcc
CFLAGS = -pipe -mips32r2 -mtune=74kc -mdspr2 -Werror -O3 -Wall -W -D_REENTRANT -fPIC $(DEFINES)
Run Code Online (Sandbox Code Playgroud)
我在Qt-Qt Media hub中使用libexif(实际上libexif与Qt Media hub一起使用)
编辑2:一些额外的观察:我正在观察奇怪的事情.我已将print语句放在exif_get_short()中.就在回来之前
printf("return_value %d\n %u\n",exif_get_sshort (buf, order) & 0xffff, exif_get_sshort (buf, order) & 0xffff);
return (exif_get_sshort (buf, order) & 0xffff);
Run Code Online (Sandbox Code Playgroud)
我看到以下o/p:return_value 34665 34665
然后我还在调用exif_get_short()的代码中插入了print语句
....
tag = exif_get_short (d + offset + 12 * i, data->priv->order);
printf("TAG %d %u\n",tag,tag);
Run Code Online (Sandbox Code Playgroud)
我看到以下o/p:TAG -30871 4294936425
EDIT3:在MIPS板上发布exif_get_short()和exif_get_sshort()的汇编代码
.file 1 "exif-utils.c"
.section .mdebug.abi32
.previous
.gnu_attribute 4, 1
.abicalls
.text
.align 2
.globl exif_get_sshort
.ent exif_get_sshort
.type exif_get_sshort, @function
exif_get_sshort:
.set nomips16
.frame $sp,0,$31 # vars= 0, regs= 0/0, args= 0, gp= 0
.mask 0x00000000,0
.fmask 0x00000000,0
.set noreorder
.set nomacro
beq $4,$0,$L2
nop
beq $5,$0,$L3
nop
li $2,1 # 0x1
beq $5,$2,$L8
nop
$L2:
j $31
move $2,$0
$L3:
lbu $2,0($4)
lbu $3,1($4)
sll $2,$2,8
or $2,$2,$3
j $31
seh $2,$2
$L8:
lbu $2,1($4)
lbu $3,0($4)
sll $2,$2,8
or $2,$2,$3
j $31
seh $2,$2
.set macro
.set reorder
.end exif_get_sshort
.align 2
.globl exif_get_short
.ent exif_get_short
.type exif_get_short, @function
exif_get_short:
.set nomips16
.frame $sp,0,$31 # vars= 0, regs= 0/0, args= 0, gp= 0
.mask 0x00000000,0
.fmask 0x00000000,0
.set noreorder
.cpload $25
.set nomacro
lw $25,%call16(exif_get_sshort)($28)
jr $25
nop
.set macro
.set reorder
.end exif_get_short
Run Code Online (Sandbox Code Playgroud)
只是为了完整性,我的linux机器上的ASM代码
.file "exif-utils.c"
.text
.p2align 4,,15
.globl exif_get_sshort
.type exif_get_sshort, @function
exif_get_sshort:
.LFB1:
.cfi_startproc
xorl %eax, %eax
testq %rdi, %rdi
je .L2
testl %esi, %esi
jne .L8
movzbl (%rdi), %edx
movzbl 1(%rdi), %eax
sall $8, %edx
orl %edx, %eax
ret
.p2align 4,,10
.p2align 3
.L8:
cmpl $1, %esi
jne .L2
movzbl 1(%rdi), %edx
movzbl (%rdi), %eax
sall $8, %edx
orl %edx, %eax
.L2:
rep
ret
.cfi_endproc
.LFE1:
.size exif_get_sshort, .-exif_get_sshort
.p2align 4,,15
.globl exif_get_short
.type exif_get_short, @function
exif_get_short:
.LFB2:
.cfi_startproc
jmp exif_get_sshort@PLT
.cfi_endproc
.LFE2:
.size exif_get_short, .-exif_get_short
Run Code Online (Sandbox Code Playgroud)
编辑4:希望我的最后更新:-) ASM代码,编译器选项设置为-O1
exif_get_short:
.set nomips16
.frame $sp,32,$31 # vars= 0, regs= 1/0, args= 16, gp= 8
.mask 0x80000000,-4
.fmask 0x00000000,0
.set noreorder
.cpload $25
.set nomacro
addiu $sp,$sp,-32
sw $31,28($sp)
.cprestore 16
lw $25,%call16(exif_get_sshort)($28)
jalr $25
nop
lw $28,16($sp)
andi $2,$2,0xffff
lw $31,28($sp)
j $31
addiu $sp,$sp,32
.set macro
.set reorder
.end exif_get_short
Run Code Online (Sandbox Code Playgroud)
MIPS 汇编显示的一件事(尽管我不是 MIPS 汇编方面的专家,所以很有可能我遗漏了一些东西或以其他方式错误)是该exif_get_short()函数只是该exif_get_sshort()函数的别名。所做的只是exif_get_short()跳转到函数的地址exif_get_sshort()。
函数exif_get_sshort()符号将其返回的 16 位值扩展为用于返回的完整 32 位寄存器。这没有任何问题 - 它实际上可能是 MIPS ABI 指定的(我不确定)。
不过,由于exif_get_short()函数只是跳转到exif_get_sshort()函数处,所以没有机会清除寄存器的高16位。
因此,当从缓冲区返回 16 位值 0x8769 时(无论是从exif_get_sshort()或exif_get_short()),$2用于返回函数结果的寄存器包含0xffff8769,它可以有以下解释:
signed int:-30871作为 32 位 `unsigned int:4294936425
作为 16 位有符号int16_t:-30871
uint16_t:34665如果编译器应该确保$2返回寄存器的返回类型的前 16 位设置为零uint16_t,那么它在其发出的代码中存在错误- 它应该调用并清除上半部分,exif_get_short()而不是跳转到 。返回之前的。exif_get_sshort()exif_get_sshort()$2
从您所看到的行为描述来看,调用代码似乎exif_get_short()期望$2用于返回值的寄存器将清除高 16 位,以便整个 32 位寄存器可以按原样用于 16 位寄存器。 - 位uint16_t值。
我不确定 MIPS ABI 指定了什么(但我猜测它指定寄存器的高 16 位$2应由 eb 清除exif_get_short()),但似乎存在一个代码生成错误,exif_get_short()不能确保$2完全清除在返回之前进行更正,或者调用者exif_get_short()假定 的完整 32 位$2有效,而实际上只有 16 位有效。