ข้อผิดพลาดของบัสคืออะไร


254

ข้อความ "ข้อผิดพลาดของบัส" หมายความว่าอย่างไรและแตกต่างจาก segfault อย่างไร


5
ฉันต้องการเพิ่มคำอธิบายง่ายๆสำหรับ: ข้อผิดพลาดการแบ่งกลุ่มหมายความว่าคุณกำลังพยายามเข้าถึงหน่วยความจำที่คุณไม่ได้รับอนุญาต (เช่นไม่ใช่ส่วนหนึ่งของโปรแกรมของคุณ) อย่างไรก็ตามในข้อผิดพลาดของบัสหมายความว่าคุณพยายามเข้าถึงหน่วยความจำที่ไม่มีอยู่ (เช่นคุณพยายามเข้าถึงที่อยู่ที่ 12G แต่คุณมีหน่วยความจำ 8G เท่านั้น) หรือหากคุณมีหน่วยความจำเกินขีด จำกัด ที่ใช้งานได้
xdevs23

คุณเห็นสิ่งใดบนแพลตฟอร์มนี้ PC? Mac? x86? 32/64?
Peter Mortensen

คำตอบ:


243

ข้อผิดพลาดของบัสหายากในปัจจุบันใน x86 และเกิดขึ้นเมื่อโปรเซสเซอร์ของคุณไม่สามารถแม้แต่จะพยายามเข้าถึงหน่วยความจำที่ร้องขอโดยทั่วไปแล้ว:

  • การใช้คำสั่งตัวประมวลผลพร้อมที่อยู่ซึ่งไม่เป็นไปตามข้อกำหนดการจัดตำแหน่ง

ความผิดพลาดในการแบ่งกลุ่มเกิดขึ้นเมื่อเข้าถึงหน่วยความจำที่ไม่ได้อยู่ในกระบวนการของคุณซึ่งเป็นเรื่องปกติมากและมักเป็นผลมาจาก:

  • ใช้ตัวชี้ไปยังสิ่งที่ถูกจัดสรรคืน
  • ใช้ตัวชี้ไม่ปลอมดังนั้นจึง
  • โดยใช้พอยน์เตอร์พอยน์เตอร์
  • บัฟเฟอร์ล้น

PS: เพื่อให้มีความแม่นยำมากขึ้นนี้ไม่ได้จัดการตัวชี้ที่จะทำให้เกิดปัญหาก็คือการเข้าถึงหน่วยความจำที่ชี้ไปที่ (dereferencing)


105
พวกเขาไม่ได้หายาก ฉันแค่ออกกำลังกาย 9 จากวิธีการเรียนรู้วิธียากและได้พบหนึ่ง ...
11684

24
สาเหตุของข้อผิดพลาดของบัส (บน Linux ต่อไป) คือเมื่อระบบปฏิบัติการไม่สามารถสำรองเพจเสมือนด้วยหน่วยความจำกายภาพ (เช่นเงื่อนไขหน่วยความจำต่ำหรือหน้าใหญ่เมื่อใช้หน่วยความจำหน้าขนาดใหญ่) โดยทั่วไป mmap (และ malloc) จองพื้นที่ที่อยู่เสมือนและเคอร์เนลจะกำหนดหน่วยความจำกายภาพตามความต้องการ (เรียกว่าข้อบกพร่องหน้าซอฟต์) สร้าง malloc ให้ใหญ่พอจากนั้นจึงเขียนลงไปให้เพียงพอและคุณจะได้รับข้อผิดพลาดของบัส
Eloff

1
สำหรับฉันพาร์ทิชันที่มี/var/cacheเพียงเต็มaskubuntu.com/a/915520/493379
c33s

2
ในกรณีของฉันวิธีการstatic_castแก้ไขvoid *พารามิเตอร์ให้กับวัตถุที่เก็บการโทรกลับ (หนึ่งคุณลักษณะชี้ไปที่วัตถุและอื่น ๆ ที่วิธีการ) จากนั้นโทรกลับถูกเรียก อย่างไรก็ตามสิ่งที่ผ่านเป็นvoid *สิ่งที่แตกต่างกันอย่างสมบูรณ์และทำให้การเรียกวิธีการทำให้เกิดข้อผิดพลาดรถบัส
Christopher K.

@bltxd คุณรู้หรือไม่ว่าลักษณะของข้อผิดพลาดของบัส นั่นคือข้อความบนรถบัสมีกลไกบางอย่างที่หยุดบนวงแหวนก็ยอมรับข้อความที่ถูกส่งโดยมัน แต่ไปยังปลายทางใดก็ตามตามที่มันแสดงให้เห็นว่ามันได้ไปตลอดทางรอบวงแหวนและไม่ได้รับการยอมรับ ฉันคาดเดาบัฟเฟอร์การกรอกข้อมูลบรรทัดส่งกลับสถานะข้อผิดพลาดและเมื่อมัน retires มันไปป์ไลน์และเรียก microroutine ข้อยกเว้นที่ถูกต้อง โดยทั่วไปจำเป็นต้องให้ตัวควบคุมหน่วยความจำยอมรับที่อยู่ทั้งหมดในช่วงที่จะแนะนำว่าเมื่อ BARs ฯลฯ มีการเปลี่ยนแปลงก็จะต้องภายใน
Lewis Kelsey

84

segfault กำลังเข้าถึงหน่วยความจำที่คุณไม่ได้รับอนุญาตให้เข้าถึง มันเป็นแบบอ่านอย่างเดียวคุณไม่ได้รับอนุญาต ฯลฯ ...

ข้อผิดพลาดของบัสกำลังพยายามเข้าถึงหน่วยความจำที่ไม่สามารถอยู่ได้ คุณใช้ที่อยู่ที่ไม่มีความหมายต่อระบบหรือที่อยู่ผิดประเภทสำหรับการดำเนินการนั้น


14

mmap ตัวอย่าง POSIX ที่น้อยที่สุด 7

"Bus error" เกิดขึ้นเมื่อเคอร์เนลส่งSIGBUSไปยังกระบวนการ

ตัวอย่างเล็ก ๆ น้อย ๆ ที่ทำให้เกิดเพราะftruncateถูกลืม:

#include <fcntl.h> /* O_ constants */
#include <unistd.h> /* ftruncate */
#include <sys/mman.h> /* mmap */

int main() {
    int fd;
    int *map;
    int size = sizeof(int);
    char *name = "/a";

    shm_unlink(name);
    fd = shm_open(name, O_RDWR | O_CREAT, (mode_t)0600);
    /* THIS is the cause of the problem. */
    /*ftruncate(fd, size);*/
    map = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    /* This is what generates the SIGBUS. */
    *map = 0;
}

ทำงานด้วย:

gcc -std=c99 main.c -lrt
./a.out

ผ่านการทดสอบใน Ubuntu 14.04

POSIX อธิบาย SIGBUSว่า:

เข้าถึงส่วนที่ไม่ได้กำหนดของวัตถุหน่วยความจำ

ข้อมูลจำเพาะ mmapกล่าวว่า:

การอ้างอิงภายในช่วงที่อยู่เริ่มต้นที่ pa และดำเนินการต่อเพื่อ len ไบต์ไปยังหน้าทั้งหมดหลังจากสิ้นสุดวัตถุจะส่งผลให้มีการส่งสัญญาณ SIGBUS

และshm_open บอกว่ามันสร้างวัตถุขนาด 0:

วัตถุหน่วยความจำที่ใช้ร่วมกันมีขนาดเป็นศูนย์

ดังนั้นเมื่อ*map = 0เราสัมผัสถึงจุดจบของวัตถุที่จัดสรร

การเข้าถึงหน่วยความจำสแต็กที่ไม่ได้กำหนดใน ARMv8 aarch64

สิ่งนี้ถูกกล่าวถึงที่: ข้อผิดพลาดของบัสคืออะไร สำหรับ SPARC แต่ที่นี่ฉันจะให้ตัวอย่างที่ทำซ้ำได้มากขึ้น

สิ่งที่คุณต้องการคือโปรแกรม aarch64 อิสระ:

.global _start
_start:
asm_main_after_prologue:
    /* misalign the stack out of 16-bit boundary */
    add sp, sp, #-4
    /* access the stack */
    ldr w0, [sp]

    /* exit syscall in case SIGBUS does not happen */
    mov x0, 0
    mov x8, 93
    svc 0

โปรแกรมที่แล้วเพิ่ม SIGBUS บน Ubuntu 18.04 aarch64 ลินุกซ์เคอร์เนล 4.15.0 ในเครื่องเซิร์ฟเวอร์ ThunderX2

น่าเสียดายที่ฉันทำซ้ำไม่ได้ในโหมดผู้ใช้ QEMU v4.0.0 ฉันไม่แน่ใจว่าทำไม

ความผิดที่ดูเหมือนจะเป็นตัวเลือกและควบคุมโดยSCTLR_ELx.SAและSCTLR_EL1.SA0สาขาที่ผมได้สรุปเอกสารที่เกี่ยวข้องบิตต่อที่นี่


11

ฉันเชื่อว่าเคอร์เนลจะเพิ่ม SIGBUS เมื่อแอปพลิเคชันแสดงข้อมูลที่ไม่ตรงแนวบนดาต้าบัส ฉันคิดว่าเนื่องจากคอมไพเลอร์สมัยใหม่ส่วนใหญ่ [?] สำหรับโปรเซสเซอร์ส่วนใหญ่จะวางแผ่น / จัดเรียงข้อมูลสำหรับโปรแกรมเมอร์ปัญหาการจัดตำแหน่งของสมัยก่อน (อย่างน้อย) ก็ลดลงและด้วยเหตุนี้เราจึงไม่เห็น SIGBUS บ่อยเกินไปในวันนี้ (AFAIK)

จาก: ที่นี่


1
ขึ้นอยู่กับเทคนิคที่น่ารังเกียจที่คุณทำกับรหัสของคุณ คุณสามารถทริกเกอร์ข้อผิดพลาด BUS / Alignment Trap หากคุณทำอะไรโง่ ๆ เช่นทำตัวชี้คณิตศาสตร์แล้วพิมพ์ตัวต่อเพื่อเข้าสู่โหมดปัญหา (เช่นคุณตั้งค่าอาร์เรย์ uint8_t เพิ่มหนึ่งสองหรือสามตัวในอาร์เรย์แล้วพิมพ์ดีด สั้น, int, หรือ long และพยายามเข้าถึงผลลัพธ์ที่ละเมิด) ระบบ X86 จะช่วยให้คุณทำสิ่งนี้ได้แม้ว่าจะมีการลงโทษประสิทธิภาพที่แท้จริง ระบบ ARMv7 บางอย่างจะช่วยให้คุณทำเช่นนี้ - แต่ ARM, MIPS, Power และอื่น ๆ ส่วนใหญ่จะบ่นกับคุณมากกว่า
Svartalf

6

คุณยังสามารถรับ SIGBUS เมื่อเพจโค้ดไม่สามารถเพจได้ด้วยเหตุผลบางประการ


7
สิ่งนี้มักจะเกิดขึ้นเมื่อฉันอัปเดตไฟล์. so ในขณะที่ทำงานกระบวนการ
พัฒนาซอฟต์แวร์

อีกสาเหตุที่เกิดขึ้นคือถ้าคุณพยายามmmapไฟล์ที่มีขนาดใหญ่กว่า/dev/shm
ilija139

3

ตัวอย่างเฉพาะของข้อผิดพลาดเกี่ยวกับบัสที่ฉันเพิ่งพบขณะเขียนโปรแกรม C บน OS X:

#include <string.h>
#include <stdio.h>

int main(void)
{
    char buffer[120];
    fgets(buffer, sizeof buffer, stdin);
    strcat("foo", buffer);
    return 0;
}

ในกรณีที่คุณจำไม่ได้เอกสารจะต่อstrcatท้ายอาร์กิวเมนต์ที่สองเป็นอันดับแรกโดยเปลี่ยนอาร์กิวเมนต์แรก (พลิกอาร์กิวเมนต์และทำงานได้ดี) บน linux สิ่งนี้จะทำให้เกิดความผิดพลาดในการแบ่งส่วน (ตามที่คาดไว้) แต่ใน OS X จะทำให้เกิดข้อผิดพลาดของบัส ทำไม? ฉันไม่รู้จริงๆ


การป้องกันโอเวอร์โฟลว์อย่างมากอาจทำให้เกิดข้อผิดพลาดของบัส
Joshua

1
"foo"ถูกเก็บไว้ในหน่วยความจำแบบอ่านอย่างเดียวดังนั้นจึงเป็นไปไม่ได้ที่จะเขียนลงไป มันจะไม่ป้องกันการล้นสแต็คเพียงการป้องกันการเขียนหน่วยความจำ (นี่คือช่องโหว่ความปลอดภัยหากโปรแกรมของคุณสามารถเขียนใหม่เอง)
Mark Lakata

3

อินสแตนซ์คลาสสิกอย่างหนึ่งของข้อผิดพลาดของบัสอยู่ในการออกแบบสถาปัตยกรรมบางอย่างเช่นSPARC (อย่างน้อย SPARC บางอันอาจมีการเปลี่ยนแปลง) คือเมื่อคุณทำการเข้าถึงที่ไม่ถูกต้อง ตัวอย่างเช่น

unsigned char data[6];
(unsigned int *) (data + 2) = 0xdeadf00d;

ตัวอย่างนี้พยายามเขียนค่าจำนวนเต็มแบบ 32 บิต0xdeadf00dไปยังที่อยู่ซึ่งไม่น่าจะจัดตำแหน่งอย่างเหมาะสมและจะสร้างข้อผิดพลาดของบัสบนสถาปัตยกรรมที่ "จู้จี้จุกจิก" ในเรื่องนี้ Intel x86 ไม่ได้เป็นสถาปัตยกรรม แต่จะอนุญาตให้เข้าถึงได้ (แม้ว่าจะรันช้ากว่า)


1
ในกรณีที่ฉันมีข้อมูล [8]; นี่เป็นผลคูณของ 4 ในสถาปัตยกรรม 32 บิต ดังนั้นมันจึงอยู่ในแนวเดียวกัน ฉันจะยังคงได้รับข้อผิดพลาดตอนนี้หรือไม่ นอกจากนี้โปรดอธิบายว่าเป็นความคิดที่ไม่ถูกต้องหรือไม่ที่การแปลงชนิดข้อมูลสำหรับพอยน์เตอร์ มันจะทำให้เกิดข้อผิดพลาดการจัดตำแหน่งที่ผิดพลาดในสถาปัตยกรรมที่เปราะบางหรือไม่ โปรดอธิบายอย่างละเอียดมันจะช่วยฉัน
กระฉับกระเฉง

หึ มันไม่ได้เป็นประเภทการแปลงมากนักในขณะที่คุณกำลังทำการแปลงรูปแบบบนพอยน์เตอร์ที่คุณทำคณิตศาสตร์พอยน์เตอร์อยู่ ดูอย่างระมัดระวังในรหัสข้างต้น คอมไพเลอร์ได้จัด dword อย่างแม่นยำจัดตำแหน่งตัวชี้ของคุณสำหรับข้อมูล - จากนั้นคุณไขสกรูทุกอย่างขึ้นไปบนคอมไพเลอร์โดยทำการออฟเซ็ตการอ้างอิงโดย TWO และ typecasting ไปยังการเข้าถึงที่ถูกต้อง
Svartalf

"เปราะบาง" ไม่ใช่คำที่ฉันใช้กับสิ่งนี้ทั้งหมด เครื่องจักรและรหัส X86 ทำให้ผู้คนทำสิ่งที่ค่อนข้างงี่เง่าอยู่พักหนึ่งนี่เป็นหนึ่งในนั้น ทบทวนรหัสของคุณใหม่ถ้าคุณมีปัญหาแบบนี้ - มันไม่ได้มีประสิทธิภาพมากนักใน X86 เริ่มต้นด้วย
Svartalf

@Svartalf: ใน x86 การเข้าถึงคำที่ไม่ตรงกับตัวชี้จะช้ากว่าการเข้าถึงคำที่ใช้ตัวชี้ที่เรียงตัวกัน แต่อย่างน้อยในอดีตพวกมันเร็วกว่าโค้ดง่าย ๆ ซึ่งจะรวบรวมสิ่งต่าง ๆ ออกมาจากไบต์ เพื่อใช้การผสมผสานที่เหมาะสมที่สุดของการทำงานขนาดต่าง ๆ ฉันหวังว่ามาตรฐาน C จะรวมถึงวิธีการบรรจุ / แกะกล่องจำนวนเต็มขนาดใหญ่ไปยัง / จากลำดับของจำนวนเต็ม / ตัวอักษรขนาดเล็กเพื่อให้คอมไพเลอร์ใช้วิธีการใดก็ตามที่ดีที่สุดบนแพลตฟอร์มที่กำหนด
supercat

@ Supercat: นี่คือสิ่งนี้ - คุณจะไปกับมันใน X86 คุณลองทำสิ่งนี้กับ ARM, MIPS, Power และอื่น ๆ และคุณจะได้รับสิ่งที่น่ารังเกียจเกิดขึ้นกับคุณ บน ARM น้อยกว่า Arch V7 คุณจะมีรหัสของคุณมีความล้มเหลวในการจัดตำแหน่งและใน V7 คุณสามารถถ้ารันไทม์ของคุณถูกตั้งค่าให้จัดการกับมันด้วยประสิทธิภาพการทำงานที่รุนแรง คุณเพียงแค่ไม่ต้องการทำสิ่งนี้ มันเป็นการปฏิบัติที่ไม่ดีที่จะทื่อ : D
Svartalf

2

ขึ้นอยู่กับ OS, CPU, Compiler และปัจจัยอื่น ๆ

โดยทั่วไปหมายถึง CPU bus ไม่สามารถดำเนินการตามคำสั่งหรือประสบความขัดแย้ง แต่อาจหมายถึงสิ่งต่าง ๆ ทั้งหมดขึ้นอยู่กับสภาพแวดล้อมและรหัสที่ใช้งาน

อดัม


2

ปกติแล้วหมายถึงการเข้าถึงที่ไม่จัดชิด

ความพยายามในการเข้าถึงหน่วยความจำที่ไม่ได้มีอยู่จริงจะทำให้เกิดข้อผิดพลาดกับบัส แต่คุณจะไม่เห็นสิ่งนี้หากคุณใช้โปรเซสเซอร์ที่มี MMU และระบบปฏิบัติการที่ไม่ใช่รถเข็นเพราะคุณจะไม่มี หน่วยความจำที่มีอยู่แมปไปยังพื้นที่ที่อยู่ของกระบวนการของคุณ


2
i7 ของฉันมี MMU อย่างแน่นอน แต่ฉันยังคงพบข้อผิดพลาดนี้ในขณะที่เรียนรู้ C บน OS X (ผ่านตัวชี้ที่ไม่กำหนดค่าเริ่มต้นscanf) นั่นหมายความว่า OS X Mavericks เป็นรถหรือเปล่า สิ่งที่จะได้รับพฤติกรรมในระบบปฏิบัติการที่ไม่ใช่รถ?
Calvin Huang

2

ฉันได้รับข้อผิดพลาดของบัสเมื่อไดเรกทอรีรากอยู่ที่ 100%


1

เหตุผลของฉันสำหรับข้อผิดพลาดของบัสบน Mac OS X คือฉันพยายามจัดสรรประมาณ 1Mb ในสแต็ก นี้ทำงานได้ดีในหนึ่งด้าย แต่เมื่อใช้ OpenMP ไดรฟ์นี้มีข้อผิดพลาดรถบัสเพราะ Mac OS X ได้ จำกัด มากขนาดสแต็คสำหรับหัวข้อที่ไม่ใช่หลัก


1

ฉันเห็นด้วยกับคำตอบทั้งหมดข้างต้น นี่คือ 2 เซ็นต์ของฉันเกี่ยวกับข้อผิดพลาด BUS:

ข้อผิดพลาด BUS ไม่จำเป็นต้องเกิดขึ้นจากคำแนะนำภายในรหัสของโปรแกรม สิ่งนี้สามารถเกิดขึ้นได้เมื่อคุณรันไบนารีและในระหว่างการประมวลผลไบนารีจะถูกแก้ไข (เขียนทับโดยบิลด์หรือลบเป็นต้น)

การตรวจสอบว่าเป็นกรณีนี้: วิธีง่ายๆในการตรวจสอบว่านี่คือสาเหตุหรือไม่โดยเรียกใช้อินสแตนซ์ของไบนารีเดียวกันและเรียกใช้บิลด์ ทั้งอินสแตนซ์ที่กำลังทำงานอยู่จะทำงานล้มเหลวโดยมีSIGBUSข้อผิดพลาดหลังจากสร้างเสร็จและแทนที่ไบนารี (อินสแตนซ์ที่ทั้งสองอินสแตนซ์กำลังทำงานอยู่)

เหตุผลพื้นฐาน: นี่เป็นเพราะ OS สลับหน้าหน่วยความจำและในบางกรณีไบนารีอาจไม่ได้โหลดอย่างสมบูรณ์ในหน่วยความจำและความล้มเหลวเหล่านี้จะเกิดขึ้นเมื่อระบบปฏิบัติการพยายามดึงหน้าถัดไปจากไบนารีเดียวกัน แต่ไบนารีมีการเปลี่ยนแปลงตั้งแต่สุดท้าย อ่านมัน


เห็นด้วยนี่เป็นสาเหตุที่พบบ่อยที่สุดของความผิดพลาดของบัสในประสบการณ์ของฉัน
itaych

0

หากต้องการเพิ่มคำตอบ blxtd ข้างต้นข้อผิดพลาดของบัสก็เกิดขึ้นเช่นกันเมื่อกระบวนการของคุณไม่สามารถเข้าถึงหน่วยความจำของ 'ตัวแปร' ที่เฉพาะเจาะจงได้

for (j = 0; i < n; j++) {
    for (i =0; i < m; i++) {
        a[n+1][j] += a[i][j];
    }
}

สังเกตว่าการใช้ตัวแปร 'i ' โดยไม่ตั้งใจใน'for loop' ครั้งแรก นั่นคือสิ่งที่ทำให้เกิดข้อผิดพลาดของรถบัสในกรณีนี้


ถ้า m> = n วงรอบนอกจะทำงานหนึ่งครั้งหรือไม่ขึ้นอยู่กับค่า preexisting ของ i หาก m <n มันจะทำงานอย่างไม่มีกำหนดเมื่อดัชนี j เพิ่มขึ้นจนกว่าคุณจะพบว่าขอบเขตของอาร์เรย์ของคุณหมดและมีแนวโน้มมากที่สุดที่จะทำให้เกิดความผิดพลาดในการแบ่งกลุ่มไม่ใช่ข้อผิดพลาดของบัส หากรหัสนี้รวบรวมแล้วไม่มีปัญหาในการเข้าถึงหน่วยความจำของตัวแปร 'i' เอง ขออภัยคำตอบนี้ผิด
itaych

0

ฉันเพิ่งค้นพบวิธีที่ยากที่สุดในโปรเซสเซอร์ ARMv7 คุณสามารถเขียนโค้ดบางส่วนที่ให้การแบ่งส่วนที่ผิดพลาดเมื่อไม่ได้เพิ่มประสิทธิภาพ แต่มันทำให้คุณเกิดข้อผิดพลาดกับบัสเมื่อคอมไพล์ด้วย -O2 (ปรับให้เหมาะสมมากขึ้น)

ฉันกำลังใช้ GCC ARM gnueabihf cross compiler จาก Ubuntu 64 bit


คำถามนี้จะตอบคำถามได้อย่างไร
Peter Mortensen

-1

บัฟเฟอร์ล้นทั่วไปซึ่งส่งผลให้เกิดข้อผิดพลาด Bus คือ

{
    char buf[255];
    sprintf(buf,"%s:%s\n", ifname, message);
}

ที่นี่หากขนาดของสตริงในเครื่องหมายคำพูดคู่ ("") มากกว่าขนาด buf จะทำให้เกิดข้อผิดพลาดของบัส


1
เฮ้ ... ถ้าเป็นกรณีนี้คุณจะมีปัญหาเกี่ยวกับข้อผิดพลาดของ BUS แทนที่จะเป็นข้อผิดพลาดที่ยอดเยี่ยมที่คุณอ่านเกี่ยวกับ Windows และเครื่องอื่น ๆ ข้อผิดพลาดของ BUS เกิดขึ้นจากความพยายามในการเข้าถึง "หน่วยความจำ" ที่เครื่องไม่สามารถเข้าถึงได้เนื่องจากที่อยู่ไม่ถูกต้อง (ดังนั้นคำว่า "BUS" ผิดพลาด) ซึ่งอาจเกิดจากโฮสต์ของความล้มเหลวรวมถึงการจัดตำแหน่งที่ไม่ถูกต้องและสิ่งที่คล้ายกันตราบใดที่โปรเซสเซอร์ไม่สามารถวางที่อยู่บนเส้นทางรถบัสได้
Svartalf
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.