copy引发的问题

关于strcpy, memcpy这几个函数的用途,想必学过C的人都非常了解了。然而在用的时候,怎么样才能用对呢?

当然啦,strcpy有的一个很严重的问题就是可能会出现缓冲区溢出,比如调用函数,在栈上分配的空间,经过strcpy来把返回地址替换导致的缓冲区溢出攻击。所以说最好使用strncpy。但是这里想说的是另外一个问题,内存重叠。首先是一个naivestrcpy实现。

1
2
3
4
5
6
char *my_strcpy(char *dest, const char *src)
{
char *ret = dest;
while (*dest++ = *src++);
return ret;
}

可以看到,在destsrc不会重叠的情况下,这么做不会出问题。但是万一destsrc重叠,那么就会出现循环无法退出的情况,直至非法访存导致segmentation fault或者导致奇怪的结果。然而查阅文档可以发现,其实strcpymemcpy这两个函数,都是在内存重叠的情况下未定义的。

  • strcpy

    The behavior is undefined if the strings overlap.

  • memcpy

    If the objects overlap (which is a violation of the restrict contract) (since C99), the behavior is undefined.

所以要安全的使用内存拷贝,应该考虑memmove

he objects may overlap: copying takes place as if the characters were copied to a temporary character array and then the characters were copied from the array to dest.

可以看到,在memmoveglibc-2.25实现中,先会进行判断,如果出现内存重叠,会使用从后往前拷贝的方式。这样就可以解决问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
rettype
inhibit_loop_to_libcall
MEMMOVE (a1const void *a1, a2const void *a2, size_t len)
{
unsigned long int dstp = (long int) dest;
unsigned long int srcp = (long int) src;
/* This test makes the forward copying code be used whenever possible.
Reduces the working set. */
if (dstp - srcp >= len) /* *Unsigned* compare! */
{
/* Copy from the beginning to the end. */
#if MEMCPY_OK_FOR_FWD_MEMMOVE
dest = memcpy (dest, src, len);
#else
/* If there not too few bytes to copy, use word copy. */
if (len >= OP_T_THRES)
{
/* Copy just a few bytes to make DSTP aligned. */
len -= (-dstp) % OPSIZ;
BYTE_COPY_FWD (dstp, srcp, (-dstp) % OPSIZ);
/* Copy whole pages from SRCP to DSTP by virtual address
manipulation, as much as possible. */
PAGE_COPY_FWD_MAYBE (dstp, srcp, len, len);
/* Copy from SRCP to DSTP taking advantage of the known
alignment of DSTP. Number of bytes remaining is put
in the third argument, i.e. in LEN. This number may
vary from machine to machine. */
WORD_COPY_FWD (dstp, srcp, len, len);
/* Fall out and copy the tail. */
}
/* There are just a few bytes to copy. Use byte memory operations. */
BYTE_COPY_FWD (dstp, srcp, len);
#endif /* MEMCPY_OK_FOR_FWD_MEMMOVE */
}
else
{
/* Copy from the end to the beginning. */
srcp += len;
dstp += len;
/* If there not too few bytes to copy, use word copy. */
if (len >= OP_T_THRES)
{
/* Copy just a few bytes to make DSTP aligned. */
len -= dstp % OPSIZ;
BYTE_COPY_BWD (dstp, srcp, dstp % OPSIZ);
/* Copy from SRCP to DSTP taking advantage of the known
alignment of DSTP. Number of bytes remaining is put
in the third argument, i.e. in LEN. This number may
vary from machine to machine. */
WORD_COPY_BWD (dstp, srcp, len, len);
/* Fall out and copy the tail. */
}
/* There are just a few bytes to copy. Use byte memory operations. */
BYTE_COPY_BWD (dstp, srcp, len);
}
RETURN (dest);
}