JCh*_*se2 8 ocaml garbage-collection memory-management
我在Ocaml写了一个密码管理器.为了使其尽可能安全,我想在内存中存储一个字符串(加密密钥),以便它可以被覆盖.由于Ocaml是通过值传递的,并且有一个垃圾收集器,这已经证明是困难的.我加密所有缓冲区和变量,但我仍然需要存储"会话密钥"才能执行此操作.为了防止这种情况被自动密钥搜索程序检测到或放入交换中,它是使用随机增量从缓冲区中的一堆随机数据组合而成.实际上,我需要的只是一个变量,可以在组合密钥传递到Nocrypto库之前几秒钟被覆盖...参考是否适用于此?
根据这个cornell"Refs and Arrays"页面,refs是可变的,并且与C中的指针类似.也就是说,我还发现了一个堆栈溢出答案,讨论Ocaml refs,其中答案提到"它们就像指向新分配的指针记忆".这是否意味着每一次,它只是在内存中分配一个新东西,而不是实际改变内存中的东西?如果是这样,你就无法真正"覆盖"裁判.
我遇到的其他可能的解决方案是Bigarrays和"自定义块".我不完全确定"自定义块"是否实际上是在垃圾收集范围之外分配的.他们似乎习惯于访问外部C代码.他们被垃圾收集器复制了吗?它们会被"覆盖"吗?在内存中也存在"不透明字节"和不透明对象的概念.我很难绕过这一切如何融合在一起.关于堆栈溢出的内存中自定义块的一个有用但令人困惑的(对我而言)是:自定义块是否曾在内存中复制过?答案说他们可以四处走动.即便如此,他们会被覆盖吗?
最后一个可能的解决方案是使用像Nocrypto库那样的Cstruct来存储它.他们在这个github问题中讨论它:秘密材料擦除提问者声明:
"当然,大多数关键材料是Cstruct.t,它是一个Bigarray.Array1.t,它在GC堆之外分配"
这甚至是正确的吗?如果是这样,我似乎无法找到实际执行此操作的源文件.我对Ocaml和一般的函数式编程都很陌生.如果你很好奇,我的程序就位于github:ocaml-pass
ivg*_*ivg 14
您不应在OCaml堆中存储任何秘密信息.因此,您绝不能将您的秘密复制到任何OCaml堆分配值中,因此,既不使用字节,也不使用字符串或数组,甚至是临时的.
OCaml值统一表示为标记机器字.单词的最低有效位用作标记,用于区分指针(tag = 0)和立即值(tag = 1).因此,值始终是固定大小,并且是指针或立即数.
立即值将其数据存储在字的最重要部分,即32位系统中的31位和64位系统中的63位.指针将数据存储在块中,这些块位于所谓的OCaml堆中.OCaml堆是由垃圾收集器(GC)管理的一组块.块是以标题为前缀的数据块.标头指定GC使用的数据大小和一些其他元信息.块可以包含OCaml值(指针或立即值)或不透明数据.
总结一下.所有OCaml值都表示为机器字,可以直接在数据中存储数据,也可以是指向堆分配块的指针.每个指针指向一个且仅指向一个块.几个指针可能指向同一个块.这些值被认为是物理上相等的.任何指针都没有指向某些块.这些块被称为死亡,并由GC回收.
GC通过分配,移动和取消分配块来管理块.GC本身使用一个竞技场,它可以从C内存分配器(malloc)获得,也可以通过memmap syscall直接从内核获取(取决于特定的系统和运行时).
GC是分代的,这意味着值首先分配在称为次要堆的堆的特殊区域中.次要堆是固定大小的连续内存区域,在运行时用三个指针表示:指向beg次要堆的开头的指针,指向次要堆end的末尾的指针,以及指向cur自由开头的指针小堆的区域.分配块时,cur会增加块的大小.然后用数据初始化块.当次要堆中没有更多可用空间时(即,然后end - cur小于所需的块大小),则触发次要GC循环.GC分析存储在Minor Heap中的所有块,并复制至少一个指向Major Heap的指针引用的所有块.之后,cur指针设置为beg.
在主堆中,在称为压缩的过程中也可以多次复制块.压实器可能会尝试重新排列其竞技场中的块,以实现更紧凑的堆表示.
由于OCaml GC是移动GC,它可以任意复制堆分配的数据.虽然它被称为移动,但它实际上只是复制.即,当一个块从次要堆移动到主堆时,它实际上只是位复制,因此是重复的.次要堆中的块模型可以存在任意时间量,直到被一些新分配的值覆盖.在压缩过程中移动对象时,也会复制该对象,并且在此过程中可能会或可能不会覆盖该对象.当然,不言而喻,一旦一个块死了,它仍然可以在堆中存活一段任意时间,直到GC重用.
这一切都意味着,如果一个秘密在OCaml堆中结束,它将会疯狂,因为GC可以以任意且相当不可预测的方式多次复制它.因此,我们只能以立即值或不受GC控制的区域存储秘密.如前所述,作为指针的所有OCaml值始终指向OCaml堆中的块.块可以直接包含数据,也可以包含指针本身,指向内存堆外部.所谓的自定义块,可能会或可能不会将它们的信息存储在OCaml堆中,它会依赖于每个自定义块的特定表示.例如,Bigarray库提供自定义块,用于将其有效负载存储在OCaml堆之外.因此,Bigarray是一个自定义块,它有两个字段:指针和大小.它是一个不透明的块,即GC永远不会将这两个值视为OCaml值,并且永远不会跟随大小和指针.指针指向的数据位于OCaml堆之外,并且由malloc或通过分配memmap(事实上,它可以是任意整数,甚至指向堆栈,或静态数据,它并不重要,只要因为我们将bigarray视为一ptr,len对).
这一切都使Bigarrays成为存储秘密的理想选择.我们可以肯定的是,它们不会被GC移动,我们可以覆盖它们以防止信息在被释放后泄漏.
我们应该小心,永远不要让秘密从我们安全的地方复制到OCaml堆中.这意味着,即使我们的主存储器是安全的大型存储器,如果我们将其内容复制到OCaml字符串,信息仍将泄漏.因此,如果我们首先将信息读入OCaml字符串,然后将其复制到bigarray中,则信息仍会泄漏.因此,任何使用OCaml堆分配值的接口都是不安全的,不应使用.例如,我们不能使用OCaml通道来读取或写入秘密(我们应该依赖于Unix模块提供的内存映射或无缓冲IO).而且,无论何时string从Bigarray 获取数据类型,您都可以复制数据,并获得所有后果.