托马斯小火车,考虑复制_ {收件人,发件人} _用户()-安博电竞网页版-安博电竞网址-安博电竞

电视电影明星 299℃ 0

导言

咱们对copy_{to,from}_user()接口的运用应该是再了解不过吧。根本Linux书本都会介绍它的效果。终究它是kernel space和user space交流的桥梁。全部的数据交互都应该运用相似这种接口。所以,咱们没有理由不知道接口的效果。可是废柴鬼医娘亲天才宝宝,我也从前有过以下疑问。

  • 为什么需求copy_{to,from}_user(),它终究在背面为咱们做了什么?
  • copy_{to,from}_user()和memcpy()的差异是什么,直接运用memcpy()能够吗?
  • memcpy()代替copy_{to,from}_user()是不是必定会有问题?

一会儿找回了当年困惑的自己。我所提出的每个问题,从前我也考虑过。还不止一次的考虑,每一次都有不同的主意。当然是由于从一开端就我就没有彻底了解。现在又从头回到这个沉重的论题,持续考虑这从前的问题。

温馨提示:文章代码剖析依据Linux-4.18.0,部分架构相关代码以ARM64为代表。

百家争鸣

针对以上问题当然是先百度。百度关于该问题的博客也是许多,足以看出这个问题必定困惑着一大批Linux的爱好者。关于我的查阅成果来说,观念首要分红以下两种:

  • copy_{to,from}_user()比memcpy()多了传入地址合法性校验。例如是否归于用户空间地址规模。理论上说,内核空间能够直接运用用户空间传过来的指针,即便要做数据仿制的动作,也能够直接运用memcpy(),现实上在没有MMU的体系架构上,copy_{to,from}_user()终究的完成便是运用了memcpy()。可是关于大多数有MMU的渠道,状况就有了些改动:用户空间传过来的指针是在虚拟地址空间上的,它所指向的虚拟地址空间很或许还没有真实映射到实践的物理页面上。可是这又能怎样呢?缺页导致的反常会很通明地被内核予以修正(为缺页的地址空间提交新的物理页面),拜访到缺页的指令会持续运转似乎什么都没有发作相同。但这仅仅用户空间缺页反常的行为,在内核空间这种缺页反常有必要被显式地修正,这是由内核供给的缺页反常处理函数的规划形式决议的。其背面的思维是:在内核态,假如程序企图拜访一个尚未被提交物理页面的用户空间地址,内核有必要对此坚持警托马斯小火车,考虑仿制_ {收件人,发件人} _用户()-安博电竞网页版-安博电竞网址-安博电竞惕而不能像用户空间那样毫无发觉。
  • 假如咱们确保用户态传递的指针的正确性,咱们彻底能够用memcpy()函数代替copy_{to,from}_user()。经过一些试验测验,发现运用memcpy(),程序的运转上并没有问题。因而在确保用户态指针安全的状况下,二者能够替换。

从各家博客上,观念首要会集在第一点。看起来第一点遭到咱们的广泛认可。可是,重视实践的刘涛为什么扔掉李玮珉人又得出了第二种观念,终究是实践出真知。真理终究是是把握在少数人手里呢?仍是大众的眼睛是雪亮的呢?当然,我不否定以上任何一种观念。也不能向你确保哪种观念正确。由于,我信任即便是从前无懈可击的理论,跟着时刻的推移或许特定状况的改动理论也或许不再正确。比方,牛顿的经典力学理论(如同扯得有点远)。假如要我说人话,便是:跟着时刻的推移,Linux的代码在不断的改动。或许以上的观念在从前正确。当然,也或许现在还正确。下面的剖析便是我的观念了。相同,咱们也是需求坚持置疑的情绪。下面我就抛砖引玉。

抛砖引玉

首要咱们看下memcpy()和copy_{to,from}_user()的函数界说。参数简直没有不同,都包括意图地址,源地址和需求仿制的字节size。


static __always_inline unsigned long __must_check
copy_to_user(void __user *to, const void *from, unsigned long n);

static __always_inline unsigned long __must_check
copy_from_user(void *to, const void __user *from, unsigned long n);

void *memcpy(void *dest, const void *src, size_t len);

可是,有一点咱们必定是知道炖猪蹄的。那便是memcpy()没有传入地址合法性校验。而copy_{to,from}_user()针对传入地址进行相似下面的合法性校验(简略说点,更多校验概况能够参阅代码)。

  • 假如从用户空间copy数据到内核空间,用户空间地址to及to加上copy的字节长度n有必要坐落用户空间地址空间。
  • 假如从内核空间copy数据到用户空间,当然也需求检查地址的合法性。例如,是否越界拜访或许是不是代码段的数据等等。总归全部不合法地操作都需求马上根绝。

经过简略的比照之后,咱们再看看其他的差异以及一同讨论下上面提出的2个观念。咱们先从第2个观念说起。触及实践,我仍是有点信任实践出真知。从我测验的成果来说,完成成果分红两种状况。

第一种状况的成果是:运用memcpy()测验,没有呈现问题,代码正常运转。测验代码如下(仅仅展现proc文件体系下file_operations对应的read接口函袜子数):


static ssize_t test_read(struct file *file, char __user *buf,
size_t len, loff_t *offset)
{
memcpy(buf, "test\n", 5); /* copy_to_user(buf, "test\n", 5) */

return 5;
}

咱们运用cat指令读取文件内容,cat会经过体系调用read调用test_read,而且传递的buf巨细是4k。测验很顺畅,成果很喜人。成功地读到了“test”字符串。宝骏轿车看起来,第2点观念是没缺点的。可是,咱们还需求持续验证和探求下去。由于第1个观念提托马斯小火车,考虑仿制_ {收件人,发件人} _用户()-安博电竞网页版-安博电竞网址-安博电竞到,“在内核空间这种缺页反常有必要被显式地修正”。因而咱们还需求验证的状况是:假如buf在用户空间现已分配虚拟地址空间,可是并没有树立和物理内存的详细映射联系,这种状况下会呈现内核态page fault。咱们首要需求创立这种条件,找到契合的buf,然后测验。这儿我当然没测啦。由于有测验定论(首要是由于我懒,结构这个条件我觉得比较费事)。这个测验是我的一个朋友,人称宋教师的“阿助教”阿克曼大牛。他从前做个这个试验,而且得到的定论是:即便是没有树立和物理内存的详细映射联系的buf,代码也能够正常运转。在内核态发作page fault,并被其修正(分配详细物理内存,填充页表,树立映射联系)。一起,我从代码的视点剖析,定论也是如此。

经过上面的剖析,看起来如同是memcpy()也能够正常运用,鉴于安全地考虑主张运用copy_{to,from}_user()等接口。

第二种状况的成果是:以上的测验代码并没有正常运转,而且会触发kernel oops。当然本次测验和前次测验的kernel装备选项是不相同的。这个装备项是CONFIG_ARM64_SW_TTBR0_PAN或许CONFIG_ARM64_PAN(针对ARM64渠道)。两个装备选项的功用都是阻挠内核态直接拜访用户地址空间。只不过,CONFIG_ARM64_SW_TTBR0_PAN是软件仿真完成这种功用,而CONFIG_ARM64_PAN是硬件完成功用(ARMv8.1扩展功用)。咱们以CONFIG_ARM64_SW_TTBR0_PAN作为剖析目标(软件仿真才有代码供给剖析)。BTW,假如硬件不支撑,即便装备CONFIG_ARM64_PAN也没用,只能运用软件仿真的办法。内核Kconfig部分解说如下。假如需求拜访用户空间地址需求经过相似copy_{to,from}_user()的接口,不然会导致kernel oops。

config ARM64_SW_TTBR0_PAN
bool "Emulate Privileged Access Never using TTBR0_EL1 switching"
help弓
Enabling this option prevents the kernel from accessing
user-space memory directly by pointing TTBR0_EL1 to a reserved
zeroed area and reserved ASID. The user access routines
restore the valid TTBR0_EL1 temporarily.

在翻开CONFIG_ARM64_SW_TTBR0_PAN的选项后,测验以上代码就会导致kernel oops。原因便是内核态直接拜访了用户空间地址。因而,在这种状况咱们就不能够运用memcpy()。咱们别无挑选,只能运用copy_{to,from}_user()。当然了,咱们也不是没有办法运用memcpy(),可是需求额定的操作。怎么操作呢?下一节为你揭晓。

寻根究底

已然提到了CONFIG_ARM64_SW_TTBR0_PAN的装备选项。当然我也希望了解其背面规划的原理。由于ARM64的硬件特别规划,咱们运用两个页表基地址寄存器ttbr0_el1和ttbr1_el1。处理器依据64 bit地址的高16 bit判别拜访的地址归于用户空间仍是内核空间。假如是用户空间地址则运用ttbr0_el1,反之运用ttbr1_el1。因而,ARM64进程切换的时分,只需求改动ttbr0_el1的值即可。ttbr1_el1能够挑选不需求改动,由于全部的进程同享相同的内核空间地址。

当进程切换到内核态(中止,反常,体系调用等)后,怎么才干防止内核态拜访用户态地址空间呢?其实不难想出,改动ttbr0_el1的值即可,指向一段不合法的映射即可。因而,咱们为此预备了一份特别的页表,该页表巨细4k内存,其值满是0。当进程切换到内核态后,修正ttbr0_el1的值为该页表的地址即可确保拜访用户空间地址是不合法拜访。由于页表的值是不合法的。这个特别的页表内存经过链接脚本分配。

 
#define RESERVED_TTBR0_SIZE (PAGE_SIZE)

SECTIONS
{
reserved_ttbr0 = .;
. += RESERVED_TTBR0_SIZE;
swapper_pg_dir = .;
. += SWAPPER_DIR_SIZE;
swapper_pg_end = .;
}

这个特别的页表和内核页表在一同。和swapper_pg_dir仅仅差4k巨细。reserved_ttbr0地址开端的4k内存空间的内容会被清零。

当咱们进入内核态后会经过__uaccess_ttbr0_disable切换ttbr0_el1以封闭用户空间地址拜访,在需求拜访的时分经过__uaccess_ttbr0_enable翻开用户空间地址拜访。这两个宏界说也不杂乱,就以__uaccess_ttbr0_disable为例阐明原理。其界说如下:

 
.macro __uaccess_ttbr0_disable, tmp1
m托马斯小火车,考虑仿制_ {收件人,发件人} _用户()-安博电竞网页版-安博电竞网址-安博电竞rs \tmp1, ttbr1_el1 // swapper_pg_dir (1)
bic \tmp1, \tmp1, #TTBR_ASID_MASK
sub \tmp1, \tmp1, #RESERVED_TTBR0_SIZE // reserved_ttbr0 just before
// swapper_pg_dir (2)
msr ttbr0_el1, \tmp1 // set reserved TTBR0_EL1 (3)
isb
add \tmp1, \tmp1, #RESERVED_TTBR0_SIZE
msr ttbr1_el1, \tmp1 // set reserved ASID
isb
.endm
  • ttbr1_el1存储的是内核页表基地址,因而其值便是swapper_pg_dir。
  • swapper_pg_dir减去RESERVED_TTBR0_SIZE便是上面描绘的特别页表。
  • 将ttbr0_el1修正指向这个特别的页表基地址,当然能够确保后续拜访用户地址都是不合法的。

__uaccess_ttbr0_disable对应的C言语完成能够参阅这儿。怎么答应内核态拜访用户空间地址呢?也很简略,便是__uaccess_ttbr0_disable的反操作,给ttbr0_el1赋予合法的页表基地址。这儿就不必重复了。咱们现在需求知道的现实便是,在装备CONFIG_ARM64_SW_TTBR0_PAN的状况下,copy_{to,from}_user()接口会在copy之前答应内核态拜访用户空间,并在copy完毕之后封闭内核态拜访用户空间的才能py。因而,运用copy_{to,from}_user()才是正统做法。首要体现在安全性检异世之青睐究极龙查及安全拜访处理。这儿是其比memcpy()多的第一个特性,后边还会介绍另一个重要特性。

现在咱们能够回答上一节中留传的问题。怎样才干持续运用memcpy()?现在就很简略了,在memcpy()调用之前经过uaccess_enable_not_uao()答应内核态拜访用户空间地址,调用memcpy(),最终经过uaccess_disable_not_uao()封闭内核态拜访用户空间的才能。

有备无患

以上的测验用例都是树立在用户空间传递合法地址的基础上测验的,何为合法的用户空间地址?用户空间经过体系调用请求的虚拟地址空间包括的地址规模,便是合法的地址(不管是否分配物理页面树立映射联系)。已然要写一个接口程序,当然也要考虑程序的健壮性,咱们不能假定全部的用户传递的参数都是合法的。咱们应该预判不合法传参状况的发作,并提早做好预备,这便是有备无患。

咱们首要运用memcpy()的测验用例,随机传递一个不合法的地址。经过测验发现:会触发kernel oops。持续运用copy_{to,from}_user()代替memcpy()测验。测验发现:read()仅仅是回来过错,但不会触发kernel oops。这才是咱们想要的成果。终究,一个应用程序不应该触发kernel oops。这种机制的完成原理是什么呢?

咱们以copy_to_user()为例剖析。函数调用流程如下:

 
copy_to_user()->_copy_to_user()->raw_copy_to_user()->__arch_copy_to_user()

__arch_copy_to_user()在ARM64渠道是汇编代码完成,这部分代码很要害。

end.reqx5

ENTRY(__arch_copy_to_user)

uaccess_enable_not_uao x3, x4, x5

addend, x0, x2

#include "copy_template.S"

uaccess_disable_not_uao x3, x4

movx0, #0

ret

ENDPROC(__arch_copy_to_user)

.section .fixup,"ax"

.align2

9998:subx0, end, dst// bytes not copied

ret

.previous

  • uaccess_enable_not_uao和uaccess_disable_not_uao是上扭矩面提到的内核态拜访用户空间的开关。
  • copy_template.S文件是汇编完成的memcpy()的功用,稍后看看memcpy()的完成代码就清楚了。
  • .section .fixup,“ax”界说一个section,名为“.fixup”,权限是ax(‘a’可重定位的段,‘x’可履行段)。9998标号处的指令便是“有备无患”的善后处理作业。还记得copy_{to,from}_user()回来值的含义吗?回来0代表copy成功,不然回来剩下没有copy的字节数。这行代码便是核算剩下没有copy的字节数。当咱们拜访不合法的用户空间地址的时分,就必定会触发page fault。这种状况下,内核态发作的page fault并回来的时分并没有修正反常,所以必定不能回来发作反常的地址持续运转。所以,体系能够有2个挑选:第1个挑选是kernel oops,并给当时进程发送SIGSEGV信号;第2个挑选是不回来呈现反常的地址运转,而是挑选一个现已修正的地址回来。假如运用的是memcpy()就只有第1个挑选。可是copy_{to,from}_user()能够有第2个挑选。.fixup段便是为了完成这个修正功用。当copy进程中呈现拜访不合法用户空间地址的时分,do_page_fault()回来的地址变成9998标号处,此刻能够核算剩下未copy的字节长度,程序还能够持续履行。

比照前面剖析的成果,其实__arch_copy_to_user()能够近似等效如下联系。

uaccess_enable_not_uao();

memcpy(ubuf, kbuf, size); == __arch_copy_to_user(ubuf, kbuf, size);

uaccess_disable_not_uao();

先插播一条音讯,解说copy_template.S为何是memcpy()。memcpy()在ARM64渠道是由汇编代码完成。其界说在arch/arm64/lib/memcpy.S文件。

 
.weak memcpy
ENTRY(__memcpy)
ENTRY(memcpy)
#include "copy_template.S"
ret
ENDPIPROC(memcpy)
ENDPROC(__memcpy)

所以很显着,memcpy()和__memcpy()函数界说是相同的。而且memcpy()函数声明是weak,因而能够重写memcpy()函数(扯得有点远)。再扯一点,为何运用汇编呢?为何不运用lib/string.c文件的memcpy()函数呢?当然是为了优化memcpy() 的履行速度。lib/string.c文件的memcpy()函数是依照字节为单位进行copy(再好的硬件也会被粗糙的代码销毁)。可是现在的处理器根本都是32或许64位,彻底能够4 bytes或许8 bytes乃至16 bytes copy(考虑地址对齐的状况下)。能够显着提高履行速度。所以,ARM64渠道运用汇编完成。这部分常识能够参阅这篇博客《ARM64 的 memcpy 优化与完成》。

下面临沂大学数字化学校持续进入正题,再重复一遍:内核态拜访用户空间地址,假如触发page fault,只需用户空间地址合法,内核态也会像什么也没有发作相同修正反常(分配物理内存,树立页表映射联系)。可是假如拜访不合法用户空间地址,就挑选第2条路,测验救赎自己。这条路便是运用.fixup和__ex_table段。假如无力回天只能给当时进程发送SIGSEG托马斯小火车,考虑仿制_ {收件人,发件人} _用户()-安博电竞网页版-安博电竞网址-安博电竞V信号。而且,轻则kernel oops,重则panic(取决于kernel装备选项CONFIG_PANIC_ON_OOPS)。在内核态拜访不合法用户空间地址的状况下,do_page_fault()终究会跳转no_context标号处的__do_kernel_fault()。

static void __do_kernel_fault(unsigned long闻喜刘福虹 addr, unsigned int esr,
struct pt_regs *regs)
{
/*
* A毒宠佣兵王妃re we prepared to handle this kernel fault?
* We are almost certainly not prepared to handle instruction faults.
*/
if (!is_el1_instruction_abort(esr) && fixup_exception(revoicegs))
return;
/* ... */
}

fixup_exception()持续调用search_exception_tables(),其经过查找__ex_table段。__ex_table段存储exception table,每个entry存储着反常地址及其对应修正的地址。例如上述的9998: sub x0, end, dst指令的地址就会被找到并修正do_page_fault()函数的回来地址,以到达跳转修正的功用。其实查找进程是依据出问题的地址addr,查找__ex_table段(exception table)是否有对应的exception table entry,假如有就代表能够被修正。由于32位处理器和64银耳莲子羹位处理器完成方法有不同,因而咱们先从32位处理器反常表的完成原理说起。

__ex_table段的首尾地址别离是__start___ex_table和__stop___ex_table(界说在include/asm-generic/vmlinux.lds.h。这段内存能够看作是一个数组,数组的每个元素都是struct exception_table_entry类型,其记录着反常发作地址及其对应的修正地址。

 exception tables
__start___ex_table --> +---------------+
| entry |
+---------------+
| entry |
+---------------+
| ... |
+---------------+
| entry |
+---------------+
| entry |
__stop___ex_table --> +---------------+

在32位处理器上,struct exception_table_entry界说如下:

 
struct exception_table_entry {
unsigned long insn, fixup;
};

有一点需求清晰,在32位处理器上,unsigned long是4 bytes。insn和fixup别离存储反常发作地址及其对应的修正地址。依据反常地址ex_addr查找对应的修正地址(未找到回来0),其暗示代码如下:

 
unsigned long search_fixup_addr32(unsigned long ex_addr)
{
const struct exception_table_entry *e;

for (e = __start___ex_table; e < __stop___ex_table; e++)
if (ex_addr == e->insn)
retur新泰天气预报n e->fixup;

return 0;
}

在32位处理器上,创立exception table entry相对简略。针对copy_{to,from}_user()汇编代码中每一处用户空间地托马斯小火车,考虑仿制_ {收件人,发件人} _用户()-安博电竞网页版-安博电竞网址-安博电竞址拜访的指令都会创立一个entry,而且insn存储当时指令对应的地址,fixup存储修正指令对应的地址。

当64位处理器开端发展起来,假如咱们持续运用这种方法,必然需求2倍于32位处理器的内存存储exception table(由于存储一个地址需求8 bytes)。所以,kernel换用另一种方法完成。在64处理器上,struct exception_table_entry界说如下:

struct exception_table_entry {
int insn, fixup;
};

每个exception table entry占用的内存和32位处理器状况相同,因而内存占用不变。可是insn和fixup的含义发作改动。insn和fixup别离存储着反常发作地址及修正地址相关于当时结构体成员地址的偏移(有点拗口)。例如,依据反常地址ex_addr查找对应的修正地址(未找到回来0),其暗示代码如下:

unsigned long search_fixup_addr64(unsigned long ex_addr)
{
const struct exception_table_entry *e;

for (e = __start___ex_table; e < __stop___ex_table; e++)
if (ex_addr == (unsigned long)&e->insn + e->insn)
return (unsigned long)&e->fixup + e->fixup;

return 0;
}

因而,咱们的关注点便是怎么去构建exception_table_entry。咱们针对每个用户空间地址的内存拜访都需求创立一个exception table entry,并刺进__ex_table段。例如下面的汇编指令(汇编指令对应的地址是随意写的,不必纠结对错。了解原理才是王道)。

0xffff000000000000: ldr x1, [x0]
0xffff000000000004: add 托马斯小火车,考虑仿制_ {收件人,发件人} _用户()-安博电竞网页版-安博电竞网址-安博电竞x1, x1, #0x10
0xffff000000000008: ldr x2, [x0, #0x10]
/* ... */
0xffff000040000000: mov x0, #0xfffffffffffffff2 // -14
0xffff000040000004: ret

假定x0寄存器保存着用户空间地址,因而咱们需求对0xffff000000000000地址的汇编指令创立一个exception table entry,而且咱们希望当x0是不合法用户空间地址时,跳转回来的修正地址是0xffff000040000000。为了核算简略,假定这是创立第一个entry,__start___ex_table值是0xffff000080000000。那么qq通明皮肤修正器第一个exception ta玉林师范学院图书馆ble entry的insn和fixup成员的值别离是:0x80000000和0xbffffffc(这两个值都是负数)。因而,针对copy_{to,from}_user()汇编代码中每一处用户空间地址拜访的指令都会创立一个entry。所以0xffff000000000008地址处的汇编指令也需求创立一个exception table entry。

所以,假如内核态拜访不合法用户空间地址终究发作了什余生么?上面的剖析流程能够总结如下:

1.拜访不合法用户空间地址:0xffff000000000000: ldr x1, [x0]

2.MMU触发反常

3.CPU调用do_page_fault()

4.do_page_fault()调用search_exception_table()(regs->pc == 0xffff000000000000)

5.检查__ex_table段,寻觅0xffff000000000000 而且回来修正地址0xffff000040000000

6.do_page_fault()修正函数回来地址(regs->pc = 0xffff000040000000)并回来

7.程序持续履行,处理犯错状况

8.修正函数回来值x0 = -EFAULT (-14) 并回来(ARM64经过x0传递函数回来值)

总结

到了回顾总结的时分,copy_{to,from}_user()的考虑也到此完毕。咱们来个总结完毕此文。

  • 无论是内核态仍是用户态拜访合法的用户空间地址,当虚拟地址并未树立物理地址的映射联系的时分,page fault的流程简直相同,托马斯小火车,考虑仿制_ {收件人,发件人} _用户()-安博电竞网页版-安博电竞网址-安博电竞都会协助咱们请求物理内存并创立映射联系。所以这种状况下memcpy()和copy_{to,from}_user()是相似的。
  • 当内核态拜访不合法用户空间地址的时分,经过.fixup和__ex_table两个段的协助测验修正反常。这种修正反常并不是树立地址映射联系,而是修正do_page_fault()回来地址。memcpy()由于没有创立这样的段,所以memcpy()无法做到这点。
  • 在使能CONFIG_ARM64_SW_TTBR0_PAN或许CONFIG_ARM64_PAN(硬件支撑的状况下才有用)的时分,咱们只能运用copy_{to,from}_user()这种接口,直接运用memcpy()是不可的。

最终,我想说,即便在某些状况下memcpy()能够正常作业。可是,这也是不引荐的,不是杰出的编程习气。在用户空间和内核空间数据交互上,咱们有必要运用相似copy_{to,from}_user()的接口。为什么相似呢?由于还有其他的接口用于内核空间和用户空间数据交互,仅仅没有copy_{to,from}_user()知名。例如:{get,put}_apinkuser()。

标签: 套流氓张建声

  在“引擎盖维权”杀手夜空中最亮的星原唱事情中,假如最卡斯特罗初西安利之星就按顾客的超时空废物组成体系要求换车,或许就不至于闹得一发不行收拾。所以,利之星好像吸取了经验,在与车主折纸飞机第2次洽谈时,便容许车主赞同以原价

国信证券,4S汽车商店有多阴暗,它要求维护用户的权利以保守秘密-安博电竞网页版-安博电竞网址-安博电竞

  • 碧玺,海信会通过提高叠加电视的液晶质量来翻转有机发光二极管吗?-安博电竞网页版-安博电竞网址-安博电竞

    碧玺,海信会通过提高叠加电视的液晶质量来翻转有机发光二极管吗?-安博电竞网页版-安博电竞网址-安博电竞

  • 白术的功效与作用,原創讳疾忌医齐桓公附身!这艘技术性优点媲美福特级的天成航空母舰竟被限时秒杀-安博电竞网页版-安博电竞网址-安博电竞

    白术的功效与作用,原創讳疾忌医齐桓公附身!这艘技术性优点媲美福特级的天成航空母舰竟被限时秒杀-安博电竞网页版-安博电竞网址-安博电竞

  • 吾爱破解论坛,6月18日期待的热轧卷板期货交易的估计-安博电竞网页版-安博电竞网址-安博电竞

    吾爱破解论坛,6月18日期待的热轧卷板期货交易的估计-安博电竞网页版-安博电竞网址-安博电竞