yihong0618 和朋友们的频道 头像

消息来源频道

yihong0618 和朋友们的频道

@hyi0618

频道8,612 位成员公开可见0 人在线

yihong0618 和朋友们的频道

成员规模8,612 位成员
在线情况0 人在线
消息总数10,129 条消息
浏览量总数3,258,631 次浏览

在这个频道里搜索消息……

t.me/hyi0618

#os #design 作为编程语言爱好者,谈谈RCE这些bug的根源吧: 程序员没做错, Dennis Ritchie 们错了 😅
为什么“溢出”基本上是C的专属呢?因为老冯的时代,“汇编器是在浪费 CPU cycle” 这种反人性的设计,被推崇为《程序员的自我修养》 (x86汇编的应用与骇客少,不讨论单片机界的情况) #recommend
就像JS人不懂 1+"1", 1-"1" 为啥会不同,
初学C的人很奇怪为啥 1+1 可以而 "a"+"b" 却不行(还必须 strcat 或 sprintf), 'a'+'a' 更是居然==194!厘清 return struct{} 与*alloc、class Reader{}与UTF16编码,以及它们在C++的平替,就需要更久了
不过,随着对 x=1; (X==1) 以及从0开始数 for(i=0) use(a[i]) 的区分,常量级语法和"基础类型" 就不再是问题,转而学习函数对象、链表、fopen()、KV 这些有语意的类型了
但我们先回到"基础类型"。C的 struct,union,[] 是有严重安全隐患的,「这只是性能的牺牲品」?
#rust 已经告诉我们 redox-os.org/ 这样复杂的系统也能默认内存安全, Python 则证明了仅仅靠运算符重载、Rc<T> 这样幼稚的引用计数,就能组装出了不起的AI应用
struct{} 的问题是“地址相关性”。就为了与x86的一些硬件地址对接(1% cases),C让一切数据类型都与 bytes(char*) 直接对应,以便映射到某个内存页位置、通过 malloc(N) free(obj) 链表来瓜分glibc mmap(brk)到的进程内存
(alloc相当于单实例版的现代 memScoped{} 即 zig ArenaAllocator 。C的"弱类型"设计错误有多深远?直到2004 Linux 2.6.8 才支持Nx 即heap默认不可执行的"保护"..)
这就造成了 memleak, segfault(空指针和类型错误的cast, 例如把小结构cast成大结构) 甚至 "更适合C语言体制的SQL注入"
这里说的,就是子类型里的 Animal() as Duck, Rect(1,2) as Square ,要么是表格验证的语意错误,要么从语法上就不该对
struct Animal {
enum Tag{Duck,Cat}
union v {
struct Duck{int beck} //实现上会移到外部(便于在Animal{}外扩充),把继承的项外提,以及有virtual vs final 的虚表双指针
}
}
并且,哪怕你能够通过一些信息压缩掉int Tag ,用 rawtypeof<Animal> xx; xx(1).beck 也比默认强转好太多了。 "默认行为"的重要性被严重轻视了
比如,为什么ES6要推荐一个“凑字数”的 const x= 而不是 let+let mut x? 为啥 Java 到处都是 public final 🔘 Str wtf() ?
你可能是第一次意识到“subtyping” 保存着type tag,但这些真的是编程里的基础(你设计任何"前后端"时都应该考虑的),或许,只是在像XML那样复杂的上层框架里这并不直观。
int a[]={1,2}; 这样的“数组”,它的长度只能用静态的sizeof得知,也就是 a[N] 里N必须是个常数,对它的求值才是安全的。C99(ISO9899,P61)甚至专门定义了“退化到指针” 和'\0'结尾字符串的行为-它还好意思把这当特性! gcc 实现了常量区间的越界检查,这是说谎:
range(A=0,B) 类型的缺失从一开始就是个错误。实际上,2int=1long ,区间slice有的内存开销,与它能带来的软件安全是不成比例的。
gcc的"静态检查"只是对stdlib的缺陷涂脂抹粉,并没有消除过度追求低内存,所带来的隐患
并且,只要区分定义 a[int32_t], a[index_t] 的实现,对后者只定义一些编译期检查(private constructor),就既安全又高效。 连Ruby 💎和WebKit的作者们都知道可以这样("污点值" taint 和 TrustedTypes),今天有人称其为 contract{}.. 也行吧
然而,即便有了OS的ASLR/Nx保护,stack/heap overflow 今天还是个问题。如果你写 sprintf(buf, "%d",0) 编译器会提醒你改用 sprintf_s 或cout,sstream<<
所以 main(int narg, char** aarg){} 用什么替换? main(vector<str>) 吗? 恭喜你重新发明了“很慢的” Python,只是还没有它好用
与for和if 对应的数据结构!!它们的重要性无需多言: 如果数据能随意纂改,fn{} 这些代码块、甚至只是算式 的正确语意就无法得到保障。
至少你还能庆幸,与 main(..) 等调用对应的 cli_args a0={.raw = new string[]{"/bin/echo",}} "构造器"只是不太好写🥰,没有RCE那么严重的缺陷

OOP只不过是把C的病态类型修了一修,它自己还有“读json都要搞反射,但Class对象被认为性能低”所造成的一堆问题
log4j 们是把bug的产生从“代码的语法” “内存结构的语法”往上提高了一层,并没有消灭问题。 能消灭问题的,恐怕只有少写代码,多思考吧。
从某种意义上,缓冲区溢出、反射低性能、线程(ret2调用栈)与 async(ret2回应函数)间的鸿沟,都是『两种语言问题』,即C,Java,JS 这样的语言为了解决框架们的需求
它们原来的心智模型,无法跨越 编译期/运行期、 堆内存/栈内存/json、本机/远程、可信数据/需验证数据,甚至仅仅是 正确/错误 (Object/Error和null) 而一致地存在,就产生了许多需要学习的class libs,以及与它们的宣传不相称的复杂性和log4j那种bug
我最近在设计的逻辑式编程范式,就是为了从心智模型的层面统一这所有的『两种语言」。 祝我成功吧 🌚
实际上呢, linux 这些内核所提供的虚拟内存段(vma https://t.me/dsuse/19844) 实现,通过把内存解释为对象图,这种4K页隔离也完全能删掉, ELF, SHM, CRIU.org 那些高深的结构也就会和普通的JVM对象(对比下"class" PATH?)、多线程、序列化 一样朴素
因为C会和WASM那样安全,没有驱动程序的“引用”,就不能越过内核调用rm -rf 那种事情(yinwang 2013)。 所以fd,socketfd 这些“RPC对象号” 也就不必存在; 线程就只是让while(1) 每隔几千次暂停1次的协程,调度器、主循环evpoll 也可以消失。
用 eBPF 的人都知道我在说什么,现在有足够多的中间表示比x86指令集更优秀,完全能从汇编上来做“虚拟化”
学内核本质上就是学"4件套",而这4项任何一个普通极客都有所认识: 👀
byte format: ELF,EXT4,boot process(cpio), .. 这是用户最关心的: exe,so,apk 互不兼容,尽管他们的系统“一样高级”,C盘D盘容量一大一小,哪怕你的SSD是512G的
net: AP, Bluetooth Peering, tcp实现, 自然需要多线程和管道, 要考虑"异步" 也就是回调地狱的解决;最重要的是,Web的API和格式(FTP..)是超越运行时差异的存在,它比加了私货的SDK文档更能体现问题的本质
usb: hid键鼠触摇, hdi显示与摄像, 声音, ACPI SMART, 各种传感器.. 或许它们的“设备总线”不是USB,但我们只关心硬件的性能 而非接口!
vm: 内存和CPU的分时复用,按上面的说法,这是专门为掩饰C语言的缺陷所设计的。 难道Linux所提供的功能,比JS靠WebKit能利用的还多吗?为什么vscode只需要单线程,一些服务却总爱“加塞”,这其实就是C语言没办法“注册回调闭包” 导致的卡线程与线程池优化.. 全是与IO的业务逻辑无关的东西!
不过编程本身是无关于计算机的,就像天文无关于望远镜。
“编程赞歌是组合搭配的赞歌,框架伟大是拆分置换的伟大。代码是流动的数据,内存是暂停的程序——感想”