SegmentFault處理流程

本文將基于一個簡單的用戶態(tài)段錯誤問題畅买,簡單梳理下arm64平臺SegmentFault處理流程罗标。

1. Demo

1.1 運行環(huán)境

  • Linux + arm64平臺
  • kernel 4.9
  • gcc version 6.3.1 20170404 (Linaro GCC 6.3-2017.05)

1.2 測試程序(el0_da.c)

#include <stddef.h>

int main(int argc, char *argv[]) {
  char *p = NULL;

  *p = 1; 

  return 0;
}

反匯編如下

0000000000000000 <main>:
   0:   d10083ff    sub sp, sp, #0x20
   4:   b9000fe0    str w0, [sp,#12]
   8:   f90003e1    str x1, [sp]
   c:   f9000fff    str xzr, [sp,#24]
  10:   f9400fe0    ldr x0, [sp,#24]
  14:   52800021    mov w1, #0x1                    // #1
  18:   39000001    strb    w1, [x0]
  1c:   52800000    mov w0, #0x0                    // #0
  20:   910083ff    add sp, sp, #0x20
  24:   d65f03c0    ret

1.3 運行結(jié)果

./run.sh: line 9:  1131 Segmentation fault      (core dumped) ./el0_da

dmesg打印的kenrel log如下

$ dmesg -c
[  720.577925] [1] el0_da[1131]: unhandled level 1 translation fault (11) at 0x00000000, esr 0x92000045
[  720.587064] [1] pgd = ffffffc274af0000
[  720.590821] [1] [00000000] *pgd=0000000000000000
[  720.595271] [1] , *pud=0000000000000000
[  720.599104] [1] 
[  720.600942] [1] 
[  720.602778] [1] CPU: 1 PID: 1131 Comm: el0_da Not tainted 4.9.38-timchip-v4.3.0-00361-gd5024dc9d6f5 #1
[  720.611989] [1] Hardware name: linux,dummy-virt (DT)
[  720.616951] [1] task: ffffffc26c9fb000 task.stack: ffffffc276cf8000
[  720.623216] [1] PC is at 0x5593dfa800
[  720.626876] [1] LR is at 0x7f7dd27364
[  720.630537] [1] pc : [<0000005593dfa800>] lr : [<0000007f7dd27364>] pstate: 60000000
[  720.638273] [1] sp : 0000007fcc5b8590
[  720.641932] [1] x29: 0000007fcc5b85b0 x28: 0000000000000000 
[  720.647611] [1] x27: 0000000000000000 x26: 0000000000000000 
[  720.653287] [1] x25: 0000000000000000 x24: 0000000000000000 
[  720.658963] [1] x23: 0000000000000000 x22: 0000000000000000 
[  720.664643] [1] x21: 0000005593dfa6a0 x20: 0000000000000000 
[  720.670319] [1] x19: 0000005593dfa810 x18: 0000000000040926 
[  720.675998] [1] x17: 0000005593e0b008 x16: 0000007f7dd27288 
[  720.681675] [1] x15: 000000000000080d x14: 0000000000000000 
[  720.687351] [1] x13: 0000007f7de80028 x12: 0000007f7de80030 
[  720.693030] [1] x11: 0000040000000000 x10: 0101010101010101 
[  720.698706] [1] x9 : 03ffffffffffffff x8 : ffffffffffffffff 
[  720.704385] [1] x7 : 0000040000000000 x6 : 0000000000000000 
[  720.710061] [1] x5 : 0000000000000000 x4 : 0000007fcc5b8608 
[  720.715740] [1] x3 : 0000005593dfa7e8 x2 : 0000007fcc5b86f8 
[  720.721416] [1] x1 : 0000000000000001 x0 : 0000000000000000 
[  720.727091] [1] 

2. 處理流程

2.1 page fault

  • 用戶態(tài)進程訪問了非法地址后曹锨, CPU的MMU無法完成虛擬地址到物理地址的轉(zhuǎn)換贰锁,從而產(chǎn)生page fault異常庵朝。
  • 此后阱洪,由用戶態(tài)切換到內(nèi)核態(tài)

2.2 異常向量表

  • 源碼位于arch/arm64/kernel/entry.S
  • 用戶態(tài)觸發(fā)的訪問內(nèi)存異常问顷, 最終會進入到異常向量表的el0_sync

el0_sync如下

/*
 * EL0 mode handlers.
 */
        .align  6
el0_sync:
        kernel_entry 0
        mrs     x25, esr_el1                    // read the syndrome register
        lsr     x24, x25, #ESR_ELx_EC_SHIFT     // exception class
        cmp     x24, #ESR_ELx_EC_SVC64          // SVC in 64-bit state
        b.eq    el0_svc
        cmp     x24, #ESR_ELx_EC_DABT_LOW       // data abort in EL0
        b.eq    el0_da
        cmp     x24, #ESR_ELx_EC_IABT_LOW       // instruction abort in EL0
        b.eq    el0_ia
        cmp     x24, #ESR_ELx_EC_FP_ASIMD       // FP/ASIMD access
        b.eq    el0_fpsimd_acc
        cmp     x24, #ESR_ELx_EC_FP_EXC64       // FP/ASIMD exception
        b.eq    el0_fpsimd_exc
        cmp     x24, #ESR_ELx_EC_SYS64          // configurable trap
        b.eq    el0_sys
        cmp     x24, #ESR_ELx_EC_SP_ALIGN       // stack alignment exception
        b.eq    el0_sp_pc
        cmp     x24, #ESR_ELx_EC_PC_ALIGN       // pc alignment exception
        b.eq    el0_sp_pc
        cmp     x24, #ESR_ELx_EC_UNKNOWN        // unknown exception in EL0
        b.eq    el0_undef
        cmp     x24, #ESR_ELx_EC_BREAKPT_LOW    // debug exception in EL0
        b.ge    el0_dbg
        b       el0_inv

這里簡單解釋下

  • kernel_entry: 構(gòu)造pt_regs相關(guān)的數(shù)據(jù)(包括通用目的寄存器昂秃,sp, pc等),保存到當前內(nèi)核棧
  • esr_el1是異常診斷寄存器杜窄,用于存儲跳轉(zhuǎn)EL1的異常相關(guān)信息


    ESR

高6位是exception class, 用于標識當前異常的類型

根據(jù)前面的測試用例肠骆,esr值為0x92000045,則exception class= esr >> 26 = 0x24, 對應(yīng)ESR_ELx_EC_DABT_LOW

#define ESR_ELx_EC_DABT_LOW     (0x24)

會跳到el0_da繼續(xù)處理塞耕,el0_da的實現(xiàn)如下

el0_da:
    /*
     * Data abort handling
     */
    mrs x26, far_el1
    // enable interrupts before calling the main handler
    enable_dbg_and_irq
    ct_user_exit
    clear_address_tag x0, x26
    mov x1, x25
    mov x2, sp
    bl  do_mem_abort
    b   ret_to_user
el0_ia:

el0_da的操作

  • do_mem_abort()

    • far_el1是出錯的內(nèi)存地址蚀腿,保存到x0
    • x25是esr_el1,保存到x1
    • sp是保存的struct pt_regs基地址,保存到x2
  • ret_to_user()

    • 調(diào)用kernel_exit 0, 最終返回用戶態(tài)莉钙。

2.3 do_mem_abort

源碼位于arch/arm64/mm/fault.c

/*
 * Dispatch a data abort to the relevant handler.
 */
asmlinkage void __exception do_mem_abort(unsigned long addr, unsigned int esr,
                     struct pt_regs *regs)
{
    const struct fault_info *inf = esr_to_fault_info(esr);
    struct siginfo info;

    if (!inf->fn(addr, esr, regs))
        return;

    pr_alert("Unhandled fault: %s (0x%08x) at 0x%016lx\n",
         inf->name, esr, addr);

    info.si_signo = inf->sig;
    info.si_errno = 0;
    info.si_code  = inf->code;
    info.si_addr  = (void __user *)addr;
    arm64_notify_die("", regs, &info, esr);
}

esr_to_fault_info()函數(shù)用于從esr的低6bit取出錯誤狀態(tài)碼DFSC(Data Fault Status Code)

DFSC 說明
000000 Address size fault, level 0 of translation or translation table base register
000001 Address size fault, level 1
000010 Address size fault, level 2
000011 Address size fault, level 3
000100 Translation fault, level 0
000101 Translation fault, level 1
000110 Translation fault, level 2
000111 Translation fault, level 3
001001 Access flag fault, level 1
001010 Access flag fault, level 2
001011 Access flag fault, level 3
001101 Permission fault, level 1
001110 Permission fault, level 2
001111 Permission fault, level 3
010000 Synchronous External abort, not on translation table walk
011000 Synchronous parity or ECC error on memory access, not on translation table walk
010100 Synchronous External abort, on translation table walk, level 0
010101 Synchronous External abort, on translation table walk, level 1
010110 Synchronous External abort, on translation table walk, level 2
010111 Synchronous External abort, on translation table walk, level 3
011100 Synchronous parity or ECC error on memory access on translation table walk, level 0
011101 Synchronous parity or ECC error on memory access on translation table walk, level 1
011110 Synchronous parity or ECC error on memory access on translation table walk, level 2
011111 Synchronous parity or ECC error on memory access on translation table walk, level 3
100001 Alignment fault
110000 TLB conflict abort
110001 Unsupported atomic hardware update fault, if the implementation includes ARMv8.1-TTHM. Otherwise reserved.
110100 IMPLEMENTATION DEFINED fault (Lockdown)
110101 IMPLEMENTATION DEFINED fault (Unsupported Exclusive or Atomic access)
111101 Section Domain Fault, used only for faults reported in the PAR_EL1
111110 Page Domain Fault, used only for faults reported in the PAR_EL1

而fault_info[]是一個struct fault_info結(jié)構(gòu)體數(shù)組廓脆,對應(yīng)這64種錯誤狀態(tài)碼的處理

static const struct fault_info fault_info[] = { 
        { do_bad,               SIGBUS,  0,             "ttbr address size fault"       },
        { do_bad,               SIGBUS,  0,             "level 1 address size fault"    },  
        { do_bad,               SIGBUS,  0,             "level 2 address size fault"    },  
        { do_bad,               SIGBUS,  0,             "level 3 address size fault"    },  
        { do_translation_fault, SIGSEGV, SEGV_MAPERR,   "level 0 translation fault"     },  
        { do_translation_fault, SIGSEGV, SEGV_MAPERR,   "level 1 translation fault"     },  
        { do_translation_fault, SIGSEGV, SEGV_MAPERR,   "level 2 translation fault"     },  
        { do_page_fault,        SIGSEGV, SEGV_MAPERR,   "level 3 translation fault"     },  
        { do_bad,               SIGBUS,  0,             "unknown 8"                     },
        { do_page_fault,        SIGSEGV, SEGV_ACCERR,   "level 1 access flag fault"     },  
        { do_page_fault,        SIGSEGV, SEGV_ACCERR,   "level 2 access flag fault"     },  
        { do_page_fault,        SIGSEGV, SEGV_ACCERR,   "level 3 access flag fault"     },  
        { do_bad,               SIGBUS,  0,             "unknown 12"                    },
        { do_page_fault,        SIGSEGV, SEGV_ACCERR,   "level 1 permission fault"      },  
        { do_page_fault,        SIGSEGV, SEGV_ACCERR,   "level 2 permission fault"      },  
        { do_page_fault,        SIGSEGV, SEGV_ACCERR,   "level 3 permission fault"      },  
        { do_bad,               SIGBUS,  0,             "synchronous external abort"    },  
        { do_bad,               SIGBUS,  0,             "unknown 17"                    },
        { do_bad,               SIGBUS,  0,             "unknown 18"                    },
        { do_bad,               SIGBUS,  0,             "unknown 19"                    },
        { do_bad,               SIGBUS,  0,             "synchronous external abort (translation table walk)" },
        { do_bad,               SIGBUS,  0,             "synchronous external abort (translation table walk)" },
        { do_bad,               SIGBUS,  0,             "synchronous external abort (translation table walk)" },
        { do_bad,               SIGBUS,  0,             "synchronous external abort (translation table walk)" },
        { do_bad,               SIGBUS,  0,             "synchronous parity error"      },  
        { do_bad,               SIGBUS,  0,             "unknown 25"                    },
        { do_bad,               SIGBUS,  0,             "unknown 26"                    },
        { do_bad,               SIGBUS,  0,             "unknown 27"                    },
        { do_bad,               SIGBUS,  0,             "synchronous parity error (translation table walk)" },
        { do_bad,               SIGBUS,  0,             "synchronous parity error (translation table walk)" },
        { do_bad,               SIGBUS,  0,             "synchronous parity error (translation table walk)" },
        { do_bad,               SIGBUS,  0,             "synchronous parity error (translation table walk)" },
        { do_bad,               SIGBUS,  0,             "unknown 32"                    },
        { do_alignment_fault,   SIGBUS,  BUS_ADRALN,    "alignment fault"               },
        { do_bad,               SIGBUS,  0,             "unknown 34"                    },
        { do_bad,               SIGBUS,  0,             "unknown 35"                    },
        { do_bad,               SIGBUS,  0,             "unknown 36"                    },
        { do_bad,               SIGBUS,  0,             "unknown 37"                    },
        { do_bad,               SIGBUS,  0,             "unknown 38"                    },
        { do_bad,               SIGBUS,  0,             "unknown 39"                    },
        { do_bad,               SIGBUS,  0,             "unknown 40"                    },
        { do_bad,               SIGBUS,  0,             "unknown 41"                    },
        { do_bad,               SIGBUS,  0,             "unknown 42"                    },
        { do_bad,               SIGBUS,  0,             "unknown 43"                    },
        { do_bad,               SIGBUS,  0,             "unknown 44"                    },
        { do_bad,               SIGBUS,  0,             "unknown 45"                    },
        { do_bad,               SIGBUS,  0,             "unknown 46"                    },
        { do_bad,               SIGBUS,  0,             "unknown 47"                    },
        { do_bad,               SIGBUS,  0,             "TLB conflict abort"            },
        { do_bad,               SIGBUS,  0,             "unknown 49"                    },
        { do_bad,               SIGBUS,  0,             "unknown 50"                    },
        { do_bad,               SIGBUS,  0,             "unknown 51"                    },
        { do_bad,               SIGBUS,  0,             "implementation fault (lockdown abort)" },
        { do_bad,               SIGBUS,  0,             "implementation fault (unsupported exclusive)" },
        { do_bad,               SIGBUS,  0,             "unknown 54"                    },
        { do_bad,               SIGBUS,  0,             "unknown 55"                    },
        { do_bad,               SIGBUS,  0,             "unknown 56"                    },
        { do_bad,               SIGBUS,  0,             "unknown 57"                    },
        { do_bad,               SIGBUS,  0,             "unknown 58"                    },
        { do_bad,               SIGBUS,  0,             "unknown 59"                    },
        { do_bad,               SIGBUS,  0,             "unknown 60"                    },
        { do_bad,               SIGBUS,  0,             "section domain fault"          },
        { do_bad,               SIGBUS,  0,             "page domain fault"             },
        { do_bad,               SIGBUS,  0,             "unknown 63"                    },
};

dfsc = esr & 0x3f = 0x92000045 & 0x3f = 0x5, 對應(yīng)fault_info[]中的第5個元素"level 1 translation fault",下一步會跳到do_translation_fault()處理磁玉。

2.4 do_translation_fault

/*
 * First Level Translation Fault Handler
 *
 * We enter here because the first level page table doesn't contain a valid
 * entry for the address.
 *
 * If the address is in kernel space (>= TASK_SIZE), then we are probably
 * faulting in the vmalloc() area.
 *
 * If the init_task's first level page tables contains the relevant entry, we
 * copy the it to this task.  If not, we send the process a signal, fixup the
 * exception, or oops the kernel.
 *
 * NOTE! We MUST NOT take any locks for this case. We may be in an interrupt
 * or a critical region, and should only copy the information from the master
 * page table, nothing more.
 */
static int __kprobes do_translation_fault(unsigned long addr,
                      unsigned int esr,
                      struct pt_regs *regs)
{
    if (addr < TASK_SIZE)
        return do_page_fault(addr, esr, regs);

    do_bad_area(addr, esr, regs);
    return 0;
}

這里會跳到do_page_fault()

2.5 do_page_fault

do_page_fault()主要會調(diào)用

  • __do_page_fault()
  • __do_user_fault()

__do_page_fault()的實現(xiàn)如下

static int __do_page_fault(struct mm_struct *mm, unsigned long addr,
                           unsigned int mm_flags, unsigned long vm_flags,
                           struct task_struct *tsk)
{
        struct vm_area_struct *vma;
        int fault;

        vma = find_vma(mm, addr);
        fault = VM_FAULT_BADMAP;
        if (unlikely(!vma))
                goto out;
        if (unlikely(vma->vm_start > addr))
                goto check_stack;

        /*
         * Ok, we have a good vm_area for this memory access, so we can handle
         * it.
         */
good_area:
        /*
         * Check that the permissions on the VMA allow for the fault which
         * occurred.
         */
        if (!(vma->vm_flags & vm_flags)) {
                fault = VM_FAULT_BADACCESS;
                goto out;
        }

        return handle_mm_fault(vma, addr & PAGE_MASK, mm_flags);

check_stack:
        if (vma->vm_flags & VM_GROWSDOWN && !expand_stack(vma, addr))
                goto good_area;
out:
        return fault;
}

__do_page_fault()這里停忿, 沒有找到相應(yīng)的vma, 則會直接返回蚊伞。

前面的page fault無法處理后席赂, 若是用戶態(tài)page fault,最終會走到__do_user_fault()

2.6 __do_user_fault

static void __do_user_fault(struct task_struct *tsk, unsigned long addr,
                            unsigned int esr, unsigned int sig, int code,
                            struct pt_regs *regs)
{
        struct siginfo si;
        const struct fault_info *inf;
                
        if (unhandled_signal(tsk, sig) && show_unhandled_signals_ratelimited()) {
                inf = esr_to_fault_info(esr);
                pr_info("%s[%d]: unhandled %s (%d) at 0x%08lx, esr 0x%03x\n",
                        tsk->comm, task_pid_nr(tsk), inf->name, sig,
                        addr, esr);
                show_pte(tsk->mm, addr);
                show_regs(regs);
        }

        tsk->thread.fault_address = addr;
        tsk->thread.fault_code = esr;
        si.si_signo = sig;
        si.si_errno = 0;
        si.si_code = code;
        si.si_addr = (void __user *)addr;
        force_sig_info(sig, &si, tsk);
}

__do_user_fault()主要做幾件事:

2.6.1 打印出錯進程信息

el0_da[1131]: unhandled level 1 translation fault (11) at 0x00000000, esr 0x92000045

2.6.2 show_pte()

  • 打印pgd/pud/pmd等信息
/*
 * Dump out the page tables associated with 'addr' in mm 'mm'.
 */
void show_pte(struct mm_struct *mm, unsigned long addr)
{
        pgd_t *pgd;

        if (!mm)
                mm = &init_mm;

        pr_alert("pgd = %p\n", mm->pgd);
        pgd = pgd_offset(mm, addr);
        pr_alert("[%08lx] *pgd=%016llx", addr, pgd_val(*pgd));

        do {
                pud_t *pud;
                pmd_t *pmd;
                pte_t *pte;

                if (pgd_none(*pgd) || pgd_bad(*pgd))
                        break;

                pud = pud_offset(pgd, addr);
                printk(", *pud=%016llx", pud_val(*pud));
                if (pud_none(*pud) || pud_bad(*pud))
                        break;

                pmd = pmd_offset(pud, addr);
                printk(", *pmd=%016llx", pmd_val(*pmd));
                if (pmd_none(*pmd) || pmd_bad(*pmd))
                        break;

                pte = pte_offset_map(pmd, addr);
                printk(", *pte=%016llx", pte_val(*pte));
                pte_unmap(pte);
        } while(0);

        printk("\n");
}

2.6.3 show_regs()

  • 源碼位于arch/arm64/kernel/process.c
  • 打印PC/LR/SP/通用目的寄存器等
void __show_regs(struct pt_regs *regs)
{
        int i, top_reg;
        u64 lr, sp;

        if (compat_user_mode(regs)) {
                lr = regs->compat_lr;
                sp = regs->compat_sp;
                top_reg = 12;
        } else {
                lr = regs->regs[30];
                sp = regs->sp;
                top_reg = 29;
        }

        show_regs_print_info(KERN_DEFAULT);
        print_symbol("PC is at %s\n", instruction_pointer(regs));
        print_symbol("LR is at %s\n", lr);
        printk("pc : [<%016llx>] lr : [<%016llx>] pstate: %08llx\n",
               regs->pc, lr, regs->pstate);
        printk("sp : %016llx\n", sp);

        i = top_reg;

        while (i >= 0) {
                printk("x%-2d: %016llx ", i, regs->regs[i]);
                i--;

                if (i % 2 == 0) {
                        pr_cont("x%-2d: %016llx ", i, regs->regs[i]);
                        i--;
                }

                pr_cont("\n");
        }
        printk("\n");
}

void show_regs(struct pt_regs * regs)
{
        printk("\n");
        __show_regs(regs);
}

show_regs_print_info()相關(guān)

  • 源碼位于kernel/printk/printk.c
  • 用于打印通用的debug信息
void dump_stack_print_info(const char *log_lvl)
{
        printk("%sCPU: %d PID: %d Comm: %.20s %s %s %.*s\n",
               log_lvl, raw_smp_processor_id(), current->pid, current->comm,
               print_tainted(), init_utsname()->release,
               (int)strcspn(init_utsname()->version, " "),
               init_utsname()->version);

        if (dump_stack_arch_desc_str[0] != '\0')
                printk("%sHardware name: %s\n",
                       log_lvl, dump_stack_arch_desc_str);

        print_worker_info(log_lvl, current);
}

/**
 * show_regs_print_info - print generic debug info for show_regs()
 * @log_lvl: log level
 *
 * show_regs() implementations can use this function to print out generic
 * debug information.
 */
void show_regs_print_info(const char *log_lvl)
{
        dump_stack_print_info(log_lvl);

        printk("%stask: %p task.stack: %p\n",
               log_lvl, current, task_stack_page(current));
}

2.6.4 force_sig_info()

  • 源碼位于source/kernel/signal.c
  • 用于向進程發(fā)送信號信息
/*
 * Force a signal that the process can't ignore: if necessary
 * we unblock the signal and change any SIG_IGN to SIG_DFL.
 *
 * Note: If we unblock the signal, we always reset it to SIG_DFL,
 * since we do not want to have a signal handler that was blocked
 * be invoked when user space had explicitly blocked it.
 *
 * We don't want to have recursive SIGSEGV's etc, for example,
 * that is why we also clear SIGNAL_UNKILLABLE.
 */
int
force_sig_info(int sig, struct siginfo *info, struct task_struct *t)
{
    unsigned long int flags;
    int ret, blocked, ignored;
    struct k_sigaction *action;

    spin_lock_irqsave(&t->sighand->siglock, flags);
    action = &t->sighand->action[sig-1];
    ignored = action->sa.sa_handler == SIG_IGN;
    blocked = sigismember(&t->blocked, sig);
    if (blocked || ignored) {
        action->sa.sa_handler = SIG_DFL;
        if (blocked) {
            sigdelset(&t->blocked, sig);
            recalc_sigpending_and_wake(t);
        }
    }
    if (action->sa.sa_handler == SIG_DFL)
        t->signal->flags &= ~SIGNAL_UNKILLABLE;
    ret = specific_send_sig_info(sig, info, t);
    spin_unlock_irqrestore(&t->sighand->siglock, flags);

    return ret;
}

static int
specific_send_sig_info(int sig, struct siginfo *info, struct task_struct *t)
{
    return send_signal(sig, info, t, 0);
}

3. 總結(jié)

  • 本文通過簡單例子时迫,分析SegmentFault的處理流程
  • 針對SegmentFault問題颅停,可以借助gdb在線分析進程或離線分core dump等,來定位具體出錯的地方掠拳。

程序員自我修養(yǎng)(ID: dumphex)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末癞揉,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子碳想,更是在濱河造成了極大的恐慌烧董,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,542評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件胧奔,死亡現(xiàn)場離奇詭異逊移,居然都是意外死亡,警方通過查閱死者的電腦和手機龙填,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評論 3 394
  • 文/潘曉璐 我一進店門胳泉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人岩遗,你說我怎么就攤上這事扇商。” “怎么了宿礁?”我有些...
    開封第一講書人閱讀 163,912評論 0 354
  • 文/不壞的土叔 我叫張陵案铺,是天一觀的道長。 經(jīng)常有香客問我梆靖,道長控汉,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,449評論 1 293
  • 正文 為了忘掉前任返吻,我火速辦了婚禮姑子,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘测僵。我一直安慰自己街佑,他們只是感情好,可當我...
    茶點故事閱讀 67,500評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著沐旨,像睡著了一般森逮。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上磁携,一...
    開封第一講書人閱讀 51,370評論 1 302
  • 那天吊宋,我揣著相機與錄音,去河邊找鬼颜武。 笑死,一個胖子當著我的面吹牛拖吼,可吹牛的內(nèi)容都是我干的鳞上。 我是一名探鬼主播,決...
    沈念sama閱讀 40,193評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼吊档,長吁一口氣:“原來是場噩夢啊……” “哼篙议!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起怠硼,我...
    開封第一講書人閱讀 39,074評論 0 276
  • 序言:老撾萬榮一對情侶失蹤鬼贱,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后香璃,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體这难,經(jīng)...
    沈念sama閱讀 45,505評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,722評論 3 335
  • 正文 我和宋清朗相戀三年葡秒,在試婚紗的時候發(fā)現(xiàn)自己被綠了姻乓。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,841評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡眯牧,死狀恐怖蹋岩,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情学少,我是刑警寧澤剪个,帶...
    沈念sama閱讀 35,569評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站版确,受9級特大地震影響扣囊,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜阀坏,卻給世界環(huán)境...
    茶點故事閱讀 41,168評論 3 328
  • 文/蒙蒙 一如暖、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧忌堂,春花似錦盒至、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽樱衷。三九已至,卻和暖如春酒唉,著一層夾襖步出監(jiān)牢的瞬間矩桂,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評論 1 269
  • 我被黑心中介騙來泰國打工痪伦, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留侄榴,地道東北人。 一個月前我還...
    沈念sama閱讀 47,962評論 2 370
  • 正文 我出身青樓网沾,卻偏偏與公主長得像癞蚕,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子辉哥,可洞房花燭夜當晚...
    茶點故事閱讀 44,781評論 2 354

推薦閱讀更多精彩內(nèi)容