Hardware enforcement of boundaries for control, space, time, modularity, reference, initializability, and mutability aspects of software
Hard Objects enforce causal locality in software by annotating addresses and operations, ensuring memory safety and security, addressing the limitations of current systems by providing localized control and reducing software complexity.
Patent Information
- Application Number
- JP2021563210
- Authority / Receiving Office
- JP · JP
- Patent Type
- Patents
- Current Assignee / Owner
- Priority Date
- 2019-04-22
- Filing Date
- 2020-04-22
- Publication Date
- 2025-12-16
- Estimated Expiration
- 2040-04-22
AI Technical Summary
Existing software systems lack sufficient mechanisms to enforce causal locality, leading to security and correctness issues due to unrestricted access and manipulation of data, which current solutions either isolate address spaces excessively, impose cumbersome type safety, or require tedious formal verification.
Implement Hard Objects, which enforce causal locality through annotations and checks on instruction and data addresses, module identifiers, and memory access operations, ensuring that program execution adheres to defined boundaries and constraints.
Hard Objects provide absolute memory safety and security by allowing modules to enforce localized control, spatial, temporal, modular, and reference locality, reducing the complexity of software design and eliminating the need for garbage collection, while maintaining compatibility with existing systems.
Smart Images

Figure 0007786844000135 
Figure 0007786844000136 
Figure 0007786844000137
Abstract
Description
[Technical Field]
[0001] REFERENCE TO RELATED APPLICATIONS This application claims one or more inventions disclosed in U.S. Provisional Application No. 62 / 837,145, entitled "Hard Object: Hardware Enforcement of Boundaries on the Control, Space, Time, Modularity, Reference, and Mutability Aspects of Software," filed April 22, 2019. The benefit of the U.S. provisional application is hereby claimed under 35 U.S.C. §119(e), and the foregoing application is hereby incorporated by reference herein.
[0002] The present invention relates to the field of microprocessor systems, and more particularly, the present invention is directed to hardware enforcement of boundaries on control, space, time, modularity, reference, initializability, and mutability aspects of software implemented within a microprocessor.
[0003] This application is related to the following patents filed by one or more of the present inventors and are hereby incorporated by reference herein: U.S. Patent No. 8,364,910, U.S. Patent No. 9,934,166, U.S. Patent No. 9,569,612, and U.S. Patent No. 9,935,975. [Background technology]
[0004] Software correctness and security are crucial to modern computing infrastructure: today people use their mobile phones for banking, today computers drive cars, and moreover, in a networked world, software from different authors often cooperate closely on the same machine: plugins, mashups, and mobile code are increasingly common examples.
[0005] Building software in which abstraction violations, whether by mistake or malicious intent, can never occur is beyond the current state of the art. Today, all attempts to achieve partial solutions to this problem exhibit one of three pathologies: the extreme isolation of separate address spaces, the clumsy sluggishness of type-safe runtimes, or the nightmarish tediousness of formal verification.
[0006] As a result, areas of software are worm-eaten and insecure. Engineers who build machines made of atoms (rather than bits) rely on locality of causality to ensure that the machine behaves correctly in the presence of faults or attacks: a car has a firewall between the engine and the driver; a house has walls and lockable doors between the inside and the outside.
[0007] However, hardware engineers have worked diligently to eliminate all locality of causality in software: in modern computers, any instruction can access any data within a given memory space. Hardware engineers did this because giving software engineers this freedom made it easier for them to write the programs they wanted, but having too much of this freedom also made it easier to write programs they didn't really want. Summary of the Invention [Problem to be solved by the invention]
[0008] The correctness problem is large and intractable, so strategies are needed that empower software engineers to enforce sufficient causal locality in software so that the correctness problem becomes tractable. [Means for solving the problem]
[0009] According to one embodiment of the present invention, a method for regulating program execution on a computer is disclosed, the computer having instruction addresses and data addresses, the method including: providing instructions to at least some of the instruction addresses, where at least some of the instruction addresses are annotated with a module identifier; providing data to at least some of the data addresses, where at least some of the data addresses are annotated with a mod-module identifier and at least some of the data addresses are annotated with a text data operation suffix length; processing at least one text data operation of the instruction addresses using a target data address from among the data addresses; checking whether the module identifier annotated to the instruction address matches the module identifier annotated to the target data address except for a right-most bit of the text data operation suffix length bit length annotated to the target data address; and generating a fault if the module identifier annotated to the instruction address does not match the module identifier annotated to the target data address.
[0010] According to another embodiment of the present invention, a method for regulating program execution on a computer using memory locations is disclosed, the computer having instruction addresses, data addresses, and memory locations, and an absolute pointer with a target data address and an operator target flag, the method including: providing data to at least a portion of the data addresses, at least a portion of the data being annotated with a module owner identifier; providing instructions to at least a portion of the instruction addresses, at least a portion of the instructions being annotated with the module owner identifier, the memory access instructions accessing at least a portion of the data through the absolute pointer; processing the memory access instructions to access the data through the absolute pointer; determining, as a first check, whether the module owner identifier annotated to the instruction address is equal to the module owner identifier annotated to the target data address of the absolute pointer; and checking, as a second check, whether the operator target flag annotated to the absolute pointer is set to true, thereby generating a fault if both the first check and the second check fail.
[0011] According to another embodiment of the present invention, a method for regulating program execution on a computer using memory locations is disclosed, the computer having instruction addresses, data addresses, and data pointers, the method including: providing data to at least some of the data addresses, where at least some of the data addresses are annotated as stack memory; providing instructions to at least some of the instruction addresses, where at least some of the instructions are annotated with a module owner identifier including a memory access instruction that accesses at least some of the data through at least one of the data pointers; providing a referenceable flag annotation to at least some of the data pointers; processing the memory access instruction to access the data through at least one of the data pointers; checking whether the data address is annotated as stack memory; and checking whether the data pointer is annotated with the referenceable flag annotation set to true, whereby generating a fault if the data address is annotated as stack memory and the data pointer is not annotated with the referenceable flag annotation set to true.
[0012] According to another embodiment of the present invention, a method for regulating execution of a program on a computer using a plurality of integer registers, each having a unique register identifier memory location, is disclosed. The method includes providing an array of integer register write flags, each integer register write flag corresponding to at least one of the integer registers, providing an array of ok-to-call flags, each ok-to-call flag corresponding to a respective one of the at least one integer registers, providing a set ok-to-call flag operator having a target register identifier parameter for a target register, providing at least one operator to read a value of the target register, the target register having a target register identifier corresponding to the target register identifier parameter, providing unwritten indicator data, and processing a call instruction, thereby reading the value of the target register. When the operator that reads the value is executed, the integer register write flags are checked to determine whether the integer register write flags are set to false; if the integer register write flags are set to false, when the set-ok-to-call flag operator is invoked with the value of the target register identifier parameter, it reads an unwritten-indicator-datum instead of the value in the target register; sets an ok-to-call flag in the array of ok-to-call flags corresponding to at least one integer register having the target register identifier parameter; and when the call instruction is executed, clears to false any integer register write flags corresponding to at least one of the integer registers that corresponds to the false ok-to-call flag. [Brief explanation of the drawings]
[0013] [Figure 1a] This is a diagram that forces the data pointer durable-flag to heap-global. [Figure 1b] FIG. 10 is a diagram forcing the ref flag. [Figure 2] This diagram shows how data pointer time addresses vs. refable versions are enforced in the heap-global. [Figure 3] When loading data, mod-owner-id vs. mod-ownable-id is checked to the extent specified by may-read-suff-len. [Figure 4] When storing data, mod-owner-id vs. mod-ownable-id is checked to the extent specified by may-write-suff-len. [Figure 5] When referencing from a target data address, mod-owner-id vs. mod-ownable-id is checked to the extent specified by may-make-ref-suff-len. [Figure 6] When a call is made, the caller mod-owner-id vs. the callee mod-owner-id is checked to the extent specified by may-call-suff-len. [Figure 7] When referencing from an instruction address, the mod-owner-id of the make-reference instruction is checked against the mod-owner-id of the target instruction address to the extent specified by may-make-ref-suff-len. [Figure 8a] This is a diagram showing how to force a durable flag on a function pointer. [Figure 8b] This is a diagram showing how to force a ref-flag on a function pointer. [Figure 9] This is a diagram showing how to force func-top-flag on a function. [Figure 10] This is a diagram showing how to force the cross-module-target-flag on a function pointer. [Figure 11] A diagram of enforcing function-time vs. function refable-version on function pointers. [Figure 12] This is a diagram showing how to set current-function-start when returning. [Figure 13] This figure shows how the frame-pointer register is set upon return. [Figure 14] This diagram shows how the frame-pointer register is set on return using the framepointer-up-relative-to-stack and stack-limit registers. [Figure 15] This is a diagram of forcing the func-top-flag and function-pointer's function-body-target-flag together on a function. [Figure 16] This is a diagram that checks function-time vs. refable-version upon return. [Figure 17] This is a diagram showing how func-top-flag is checked when performing a dynamic jump. [Figure 18] This shows how to maintain the current-function-start when calling. [Figure 19] This diagram shows how a call or return sets current-function-start to target-address, then sets the value of current-function-metadata to function-start-to-function-metadata-map. [Figure 20] This diagram shows checking mod-owner-id vs. mod-ownable-id when accessing heap-global memory. [Figure 21] When accessing heap-global memory, mod-owner-id vs. mod-ownable-id is checked up to the extent specified by mod-owner-suff-len. [Figure 22] This is a diagram showing how to check the danger-flag when a dangerous operation is performed. [Figure 23]This shows that mod-owner-id is checked against mod-owner-id when making a call. [Figure 24] This is a diagram showing checking mod-owner-id vs. mod-owner-id when referencing an instruction address. [Figure 25a] This diagram shows that after a call or return instruction, the value of current-function-end is set to be the current-function-start plus the function-length of the current-function-metadata. [Figure 25b] When an instruction is attempted to execute, it checks whether the instruction address is both greater-than-or-equal-to the current-function-start and less-than the current-function-end. [Figure 26] This is a diagram showing how the time-address vs. refable-version is checked when calling. [Figure 27a] When accessing heap-global memory, object metadata is found and the target address is checked against the object range. [Figure 27b] When accessing heap-global memory, object metadata is found and the target address is checked against the object range. [Figure 27c] When accessing heap-global memory, object metadata is found and the target address is checked against the object range. [Figure 28a] When accessing heap-global memory, the page-overflow-flag is used to find the object metadata, and then the target address is checked against the object range. [Figure 28b]When accessing heap-global memory, the page-overflow-flag is used to find the object metadata, and then the target address is checked against the object range. [Figure 28c] When accessing heap-global memory, the page-overflow-flag is used to find the object metadata, and then the target address is checked against the object range. [Figure 29a] When accessing heap-global memory, the object metadata is found and the object range is checked, again using immediate-dist-to-start and immediate-length. [Figure 29b] When accessing heap-global memory, the object metadata is found and the object range is checked, again using immediate-dist-to-start and immediate-length. [Figure 29c] When accessing heap-global memory, the object metadata is found and the object range is checked, again using immediate-dist-to-start and immediate-length. [Figure 30] When accessing heap-global memory, we find the object metadata and check the object range using immediate-granularity, also using immediate-dist-to-start and immediate-length. [Figure 31a] This is a diagram showing how sub-object-start and sub-object-length are checked when accessing heap-global memory. [Figure 31b] This is a diagram showing how sub-object-start and sub-object-length are checked when accessing heap-global memory. [Figure 31c] This is a diagram showing how sub-object-start and sub-object-length are checked when accessing heap-global memory. [Figure 32a] When accessing heap-global memory, the sub-object-start and sub-object-length are checked, and the absolute sub-object-id is calculated as the sum of the sub-object-id of the absolute-pointer and the page-subobj-id-abs-base annotated to the data-page annotated to the data-page-index of the target-data-address. [Figure 32b] When accessing heap-global memory, the sub-object-start and sub-object-length are checked, and the absolute sub-object-id is calculated as the sum of the sub-object-id of the absolute-pointer and the page-subobj-id-abs-base annotated to the data-page annotated to the data-page-index of the target-data-address. [Figure 32c] When accessing heap-global memory, the sub-object-start and sub-object-length are checked, and the absolute sub-object-id is calculated as the sum of the sub-object-id of the absolute-pointer and the page-subobj-id-abs-base annotated to the data-page annotated to the data-page-index of the target-data-address. [Figure 33a] This is a diagram showing how to find the absolute sub-object-id annotated by map-subobj-topid-to-absolute-subobj-id in the sub-object-metadata-table for the subobj-topid in the absolute-pointer when accessing the heap-global memory. [Figure 33b] This is a diagram showing how to find the absolute sub-object-id annotated by map-subobj-topid-to-absolute-subobj-id in the sub-object-metadata-table for the subobj-topid in the absolute-pointer when accessing the heap-global memory. [Figure 33c] This is a diagram showing how to find the absolute sub-object-id annotated by map-subobj-topid-to-absolute-subobj-id in the sub-object-metadata-table for the subobj-topid in the absolute-pointer when accessing the heap-global memory. [Figure 34] This diagram shows how to use the public-flag when accessing heap-global memory. [Figure 35] This diagram shows how to use the writable flag when accessing the heap-global memory. [Figure 36a] This shows how to set written-flag when writing to heap-global. [Figure 36b] This shows that when reading heap-global, an error occurs unless written-flag is set. [Figure 37a] This shows how to set written-flag when writing to heap-global. [Figure 37b] When reading heap-global, the unwritten-indicator-datum is returned unless written-flag is set. [Figure 38] This diagram shows how to use the public-target-flag when accessing heap-global memory. [Figure 39] This is a diagram showing how to use the writable-target-flag when writing to heap-global memory. [Figure 40]This diagram shows that a ref-flag is required on the pointer when accessing the stack. [Figure 41] Accessing the stack through a stack-object-pointer outside the current frame requires the user of the stack-object-pointer to check that the access is within its bounds. [Figure 42] This figure shows that it is not permitted to store stack-object-pointer in heap-global. [Figure 43a] FIG. 10 illustrates the use of narrow-pointer operations to create stack-object-pointers. [Figure 43b] This diagram shows whether a call instruction is allowed or blocked depending on whether the stack-pointer is less-than-or-equal-to the stack-object-floor. [Figure 44] When returning, stack-object-floor is set to shadow-frame-pointer. [Figure 45] When returning, if the value held in the return-register is a stack-object-pointer, the stack-object-pointer-frame-pointer versus the shadow-frame-pointer is checked. [Figure 46] When returning, if the value held in the return-register is a stack-object-pointer, the stack-object-pointer-frame-pointer is calculated from the framepointer-up-relative-to-stack, and then the stack-object-pointer-frame-pointer is compared to the shadow-frame-pointer. [Figure 47]When storing the value-being-stored of one stack object pointer through another to a target data address, this diagram checks whether the stack-object-pointer-frame-pointer of the target data address is newer-than-or-equal-to the stack-object-pointer-frame-pointer of the value-being-stored. [Figure 48] When storing the value-being-stored of one stack object pointer to a target data address through another, check if the stack-object-pointer-frame-pointer of the target data address is newer-than-or-equal-to the stack-object-pointer-frame-pointer of the value-being-stored, and represent both of them using framepointer-up-relative-to-stack to represent the value-being-stored and target to the stack-limit pointer. [Figure 49a] 10 illustrates the use of a where-saved array to record where callee-save registers are saved. [Figure 49b] This diagram shows how operations other than store instructions are executed, reading the target register. [Figure 50] When a callee-save register is restored, this fact is recorded in the where-saved array. [Figure 51] When a callee-save register is restored, it is checked to see if it was restored from the correct stack location. [Figure 52] When a callee-save register is saved, a check is made to see if another callee-save register has been saved to the same location. [Figure 53]When the return instruction is executed, it is checked whether all sub-registers of the where-saved array have the value unsaved-marker-value. [Figure 54] When writing through a stack-object-pointer, check whether the pointer has a clear writable-target-flag. [Figure 55a] This diagram shows how to clear the may-restore-flag if the callee-save register is written before it is saved. [Figure 55b] When the restore-callee-save-reg-state operator is executed, if the may-restore-flag is true, it is either allowed or rejected. [Figure 56a] This figure shows that the for-this-func-flag is maintained when a call instruction is executed. [Figure 56b] When the save-callee-save-reg-state operator is executed, it sets for-this-func-flag to true. [Figure 56c] When performing an operation that accesses a target register, if that register is a callee-save register, the for-this-func-flag is checked. [Figure 56d] When the restore-callee-save-reg state operator is executed, it checks whether for-this-func-flag is true. [Figure 56e] This figure shows that when a return instruction is executed, the for-this-func-flag is maintained. [Figure 57a] When reading a register, the int-register-written-flag is checked, and if the flag is not set, it is deemed to be an error. [Figure 57b]This is a diagram showing how to use ok-to-return-flag-s to express integer registers that allow returns. [Figure 57c] Upon return, clear any int-register-written-flag that corresponds to a clear ok-to-return-flag. [Figure 58a] When reading a register, the itsint-register-written-flag is checked, and if the flag is not set, the unwritten-indicator-datum value is read. [Figure 58b] This is a diagram showing how to use ok-to-return-flag-s to express integer registers that allow returns. [Figure 58c] Upon return, any int-register-written-flag corresponding to the ok-to-return-flag is cleared. [Figure 59a] When reading a register, check to see if it has the int-register-written-flag set, and if not, raise a fault. [Figure 59b] 10 illustrates that when the set-ok-to-call-flag operator is called with the target register-id parameter value, it sets the ok-to-call-flag in the array of ok-to-call-flag-s corresponding to the integer register. [Figure 59c] 13 illustrates clearing the int-register-written-flag of any register that does not have the ok-to-call-flag set when calling a function. [Figure 60a] When reading a register, it checks whether it has the int-register-written-flag set, and if not, returns the unwritten-indicator-datum. [Figure 60b]10 illustrates that when the set-ok-to-call-flag operator is called with a target register-id parameter value, it sets the corresponding ok-to-call-flag. [Figure 60c] 13 illustrates clearing the int-register-written-flag of any register that does not have the ok-to-call-flag set when calling a function. [Figure 61a] When reading a register, check to see if it has the int-register-written-flag set, and if not, raise a fault. [Figure 61b] When the put-num-int-args-in-registers operator is called with the num-args parameter value, it sets the num-int-args-in-registers value to the num-args parameter value. [Figure 61c] This figure shows clearing the int-register-written-flag of the first num-int-args-in-registers argument register when calling a function. [Figure 62a] When reading a register, it checks whether it has the int-register-written-flag set, and if not, returns the unwritten-indicator-datum. [Figure 62b] When the put-num-int-args-in-registers operator is called with the num-args parameter value, it sets the num-int-args-in-registers value to the num-args parameter value. [Figure 62c] This figure shows clearing the int-register-written-flag of the first num-int-args-in-registers argument register when calling a function. [Figure 63a]When reading a float register, it checks whether it has the float-register-written-flag set, and if not, raises a fault. [Figure 63b] This figure shows that when the put-num-float-args-in-registers operator is called with the num-args parameter value, it sets the num-float-args-in-registers value to the num-args parameter value. [Figure 63c] This figure shows that when calling a function, the float-register-written-flag of the first num-float-args-in-registers float argument float-register is cleared. [Figure 64a] When reading a float register, it checks whether it has the float-register-written-flag set, and if not, it reads the unwritten-indicator-datum. [Figure 64b] This figure shows that when the put-num-float-args-in-registers operator is called with the num-args parameter value, it sets the num-float-args-in-registers value to the num-args parameter value. [Figure 64c] This figure shows that when calling a function, the float-register-written-flag of the first num-float-args-in-registers float argument float-register is cleared. [Figure 65a] FIG. 10 is a diagram showing how the stack floor of written data is maintained. [Figure 65b] This figure shows that when reading, if the read falls below the stack-floor, it is flagged as a failure, forcing the stack-floor. [Figure 66]Maintain stack-floor of written data; on read, force stack-floor and maintain shadow-frame-pointer; on return, set stack-floor to the value of shadow-frame-pointer. [Figure 67] This diagram shows the relationship between the central processing unit (CPU) processor core and the hard object core. DETAILED DESCRIPTION OF THE INVENTION
[0014] The invention disclosed herein is referred to as "Hard Object" ("HO"), and a specific implementation embodiment is referred to as "Dewdrop." A Hard Object: Lightweight modifications to existing computers, Compiler modifications or source-to-source transformations implemented during the software build process; and A small collection of libraries and minor modifications to existing standard system software and libraries, such as the loader, CRT0, and libc is.
[0015] Hard Objects allow program authors to enforce various kinds of causal locality in their software, including the following properties: 1. Control: The association of code with other code such that dynamic control flow movements are directed only to statically timed targets, providing a kind of locality known as structured control flow. 2. Spatial: The association of data with other data, giving a kind of locality called an object. 3. Time: The association of data with a time range between allocation and decomposition, giving a locality called object liveness. 4. Modularity: The association between data and the code that operates on it, giving a kind of locality called a module. 5. Reference: The association between a name (pointer) and the value it refers to, giving the object locality called a reference. 6. Initializability: All reads occur after the first write, providing a type of locality called initializability, where reads are associated with writes. 7. Mutability: The association between construction and computation, giving a kind of locality called constructivity, in which all writes to an object occur at object construction time, before any reads of the object.
[0016] If these properties are not sufficient to guarantee the properties in static time, dynamic checks may be added, and constraints on control flow prevent such dynamic checks from being circumvented by programs in a manner similar to Google® Native Client [google-native-client]. Indeed, the undecidability of the halting problem makes such dynamic checks necessary.
[0017] Some platforms communicate only in familiar terms: in a C program running on Unix, any data can be touched by any code in the process. Other platforms communicate only in formal terms: in a Java program, a programmer cannot implement an xor-bidirectional list, because you cannot xor pointers in Java. In contrast, Hard Objects communicate in both familiar and formal terms, letting the program author decide where the boundaries are. For example, consider an xor-bidirectional-list module that exports a pointer to a list object and also maintains an internal node object. · Format: Unlike C, modules can export format pointers to public list objects to clients, which cannot be faked. Informal: Unlike Java, modules may use internal pointers to internal Node objects that can be xored.
[0018] Hard Objects are software platforms that can communicate both formally and informally in this way, just like human natural language. External pointers cannot be faked, and so can act as object-granularity capabilities. Furthermore, while informal pointers can be faked, the modularity aspect of Hard Objects prevents clients from using these informal pointers to access objects within a module. The point is that Hard Objects provide a way to be creative by locally turning off formal constraints, if desired. The resulting modules require another method of ensuring local correctness, such as a theorem prover, but the theorems that need to be proven are local.
[0019] Hard objects protect themselves from others; they don't necessarily protect themselves from themselves (although they sometimes do in some ways). Hard objects assume that module authors act in their own self-interest, and therefore, if given the tools to protect themselves, they will do so. This assumption greatly simplifies the task, giving the programmer control over the transition between familiar and formal modes described above. That said, memory safety is guaranteed by default, and if a module turns off any aspects of memory safety, such as those required for inner nodes of an xor-bidirectional-list, doing so does not compromise the safety of another module.
[0020] This familiar-formal feature alone distinguishes Hard Objects from all other competing systems. Specifically, competing systems that attempt to provide similar services are either (1) weak: they don't solve the problem because they don't provide enough constraints (e.g., CHERI), or (2) brittle: they provide too many constraints, making them unusable for the specific task at hand, and by recognizing this, they provide loopholes (e.g., Java Native Interface calls) that, when used, immediately lose all guarantees (e.g., Java).
[0021] Hard Objects are very lightweight interventions into the hardware and software of existing system designs. The current Dewdrop design and software embodiment / implementation of Hard Objects are modifications of the prior art RISC-V® instruction set architecture 64-bit version ("RV64"), the riscv-gnu-toolchain, the RISC-V® support library, and the musl libc [musl-libc] C library. Throughout, this detailed description conveys an embodiment of Hard Objects implemented as an enhancement / modification of a prior art RISC-V® system. This detailed description expressly avoids repeating the phrase "in one embodiment" in each such instance, and therefore, the fact that a RISC-V® embodiment is merely one embodiment of Hard Objects should be understood whenever RISC-V® is mentioned.
[0022] The modifications that Hard Object makes to standard prior art systems are as follows:
[0023] Observer interface to the central processing unit (CPU): Hard objects do not need to be deeply integrated into the entire CPU. Instead, the Hard Object machine observes and intercepts the CPU's behavior at well-defined points, and observing and intercepting the CPU's state will cause the RISC-V machine to fail if it does something wrong: Hard Object watches the control flow (start / stop instructions, call, return, branch, jump) Hard Objects watch and intercept any register data flow (reads / writes to / from registers) The Hard Object watches and intercepts the inputs and outputs of the Arithmetic Logic Unit (ALU), hiding the format pointer metadata on the way to the ALU and restoring it again on the way (in one embodiment, if the Hard Object cannot do this, for example because the user has set their own metadata there, it is a failure). Hard Objects watch and intercept memory traffic (loads and stores) Hard Objects also add instructions / operations, hard-object-calls. Note that these Hard Object operations have many possible implementations: new hardware instructions, implicit behavior via read / write control status registers, or system calls to read / write metadata directly to memory.
[0024] In one embodiment, the toolchain modification is implemented entirely as a source-to-source transformation. Hard Object is so lightweight that it does not change the underlying compiler toolchain at all (there is not even a Hard-Object specific compiler: the Hard Object system simply uses the standard riscv-gcc compiler, but the build process is still enhanced by intervening with the Hard Object source-to-source transformation). While the standard compiler toolchain is unchanged, the overall build-load-run process is modified by adding further steps, most of which use source-to-source transformation. Note that in doing so, Hard Object supports almost all of the 134 optimization flags enabled by gcc -O2: C source-to-source transformations are performed after the preprocessor Assembly source-to-source transformation is performed after the compiler The linker script is edited to manage the Hard Object metadata section. Executable Source-to-Source Transformation: Executed after linking that "links" the metadata added during the preceding transformation. hard-object-sysruntime: Runs before the C-runtime (ctr0), which runs before main(). This code annotates the program with hard-object metadata before it runs.
[0025] Minor changes to standard system software and libraries: Modifications to the prior art RISC-V® pk proxy kernel used by the prior art RISC-V® Spike simulator, primarily modifying the loader and disallowing memory allocation syscalls (brk, mmap, etc.) from non-system code. It links against musl libc and primarily modifies low-level memory routines such as CRT0 and memcpy(), turning off the default memory allocator malloc() (and related functions). Observer design: The observer design allows the original chip design to remain largely unchanged, so the Hard Object can simply be attached to any CPU, such as a prior art RISC-V machine. This factoring is realized as a software hard-object-observer simulator that observes / intercepts the riscv-spike simulator.
[0026] Reduces programmer annotation burden to zero: Porting C code to Hard Objects up to the point of having memory safety is equivalent to changing a single line in a Makefile and replacing gcc with a Hard Object compiler-driver script. The only C idioms that Hard Objects currently does not automatically support, and therefore require a manual porting process, are doing non-pointer-like things with pointers (taking xor's of them) if the program has written its own memory allocator, or doing some other extremely rare thing that even highly optimized C in the real world would not do that would trigger a Hard Object special case. As programmers get smarter, the changes required to user programs are proportional to the desired smartness.
[0027] Hard Objects are absolute: When a Hard Object guarantees a property, the guarantee is absolute, not best-effort: enforcement of Hard Object properties does not depend on the assumption that hash functions never collide or that counters never rollover. Furthermore, correctness does not depend on toolchain modifications. If a user does not use Hard Object modifications to the software toolchain but instead implements Hard Object hardware, the Hard Object properties are enforced, but the program will not run because it is very easy to violate the Hard Object checks. Furthermore, one module does not need to trust another module's compiler: multiple mutually distrustful C modules may run and interact within the same address space and still be protected from each other.
[0028] Hard Objects are Hardware Native: All Hard Objects are "native" in the sense that they assume that the abstraction layers below them will not fail. Because Hard Objects do not attempt to maintain instruction / data abstractions, Hard Objects do not protect against Spectre / Meltdown / Foreshadow attacks that exploit speculative execution, nor do Hard Objects protect against cryptographic key leaks through process timing or processor heat signatures. Because Hard Objects do not attempt to maintain transistor / electronic abstractions, Hard Objects do not protect against attacks that damage the hardware itself, such as row hammers, hair dryers, or cosmic rays.
[0029] Hard Objects, like Virtual Memory before it, not only improve the correctness and security of existing software, but also enable software to be architected in new ways. Hard Objects make it easy to sandbox untrusted code. Modules are the granularity of trust: the correctness of a module depends on all of the authors of the module and all of the tools used to build it. Furthermore, the correctness of a module does not depend on the authors of other modules or the tools used to build it, except for trusted system modules.
[0030] Factoring a program into just two equal modules can make a dramatic difference in reliability. Imagine factoring a complex editor into two modules: (1) a clean graphical user interface (GUI) with only transient state, and (2) a database that holds critical document state. The GUI module can export a restart() function that forgets all of its state and re-queries it from the database module. Then, if the GUI module fails, the kernel sees the restart function and simply reboots the GUI module but leaves the database running, thus preserving critical document state. By setting the screen and cursor positions in the database as well and using double-buffering of the video to hide screen flicker, you can imagine an editor experiencing multiple null pointer exceptions per second, but the user simply continuing to work without even noticing. Editors could be designed this way today, but the coding and performance impact of factoring a program throughout the process makes doing this so heavy that programmers rarely face any trouble.
[0031] Hard Object design eliminates garbage collection as a necessary mechanism for achieving memory safety by separating the use-after-free problem from the garbage problem. Garbage collectors can never be made to work well: Hertz and Berger [hertz-berger-2005] estimate that Java garbage collection means that three to five times as much memory is needed to achieve equivalent performance to a program without garbage collection. Hertz and Berger state: "We compare explicit memory management with both copying and non-copying garbage collectors using an oracle-like memory manager against a range of benchmarks, and present actual runs (rather than simulations) to lend further validity to our results. These results quantify the time-space tradeoff of garbage collection: an Apple-style generational collector with a non-copying maturity space, with five times the memory, matches the performance of reachability-based explicit memory management. With just three times the memory, the collector runs on average 17% slower than explicit memory management. However, with just twice the memory, garbage collection performs nearly 70% slower. When physical memory becomes scarce, paging causes garbage collection to run an order of magnitude slower than explicit memory management."
[0032] The resulting version collector is algorithmically better than garbage collectors in several fundamental ways: unlike garbage collection, version collection is amenable to parallelism, concurrentness, memory hierarchy friendliness, and, if done frequently enough, it does not leave dead memory for client programs (garbage collection requires dead memory before it can do anything useful). In other words, by separating the use-after-free problem from the garbage problem, Hard Objects have a fundamentally improved situation.
[0033] Providing Capabilities: Hard Objects make it simple to turn normal objects into capabilities. Using capabilities, Hard Objects eliminate ambient privileges: code cannot access any object that it has not been given an explicit reference (in the form of a pointer). Doing this alone is a dramatic improvement in computer security. Hard Objects allow you to build software using prior art Capabilities designs at the object granularity, but Hard Objects do not require software to use Capabilities.
[0034] Kernel Decomposition: Hard Object systems, i.e., "risky" code, are much safer than prior art kernel mode. Hard Object risky code can modify metadata on any code, but its loads and stores are still subject to said metadata constraints. Therefore, static analysis of the Hard Object kernel can take advantage of the fact that Hard Object boundaries are also enforced. While risky system code is trusted, the likelihood of accidentally modifying metadata is much lower than accidentally performing a wild write.
[0035] Hard Objects make microkernels dramatically easier to build: All user code and the kernel share a single address space, eliminating Linus Torvalds' "distributed transactions" criticism of microkernels built on standard hardware. All calls / messages are simple, cross-module function calls (no message queues), thereby eliminating one major difficulty in microkernel design Kernel mode code gains the ability to modify metadata (which is always done using special-purpose instructions not used by normal code), but all other normal operations (loads and stores, etc.) are constrained just as they are for user code. Memory-mapped input / output (I / O) ports or instructions are annotated (on the hardware side) as owned by their corresponding driver modules so that device drivers can also be executed in user mode.
[0036] Hard Objects also make building exokernels simple: software running on a Hard Object system does not require a tight user-kernel boundary, and since the kernel is just another module, user code can direct access to the kernel's data structures in a safe way. Large buffers of data coming from disk or the network no longer need to be copied from kernel space to user space. Instead, the kernel simply transfers ownership of the object to the user program. Furthermore, note that existing kernels can simply be gradually incorporated into the micro-exokernel, eliminating the need to throw away existing monolithic kernels and write new ones.
[0037] One can imagine a "micro-operating system" with a micro-kernel, drivers, scheduler, file system, network stack, and portable user interface (UI) libraries (web browser widgets), and a few other things. If the kernel is so small and, for example, formally (machine-checked) proven correct, why not just burn it into the firmware? If web apps are native apps, why install apps at all? When all the refactoring is done, Hard Object systems result in something much closer to the information appliance dream.
[0038] Aesthetic design features Here are some of the meta-idioms used during the design aesthetic or choices made during the construction of a Hard Object:
[0039] Lightweight: Hard Objects attempt to be completely invisible, either to hardware or software, especially to user software: Ordinary C programs do not require user annotation burden in order to have C semantics automatically enforced, and If the program does not get a Hard Object fault, it is guaranteed to behave the same as if the Hard Object did not exist at all.
[0040] Absolute, not best-effort: All guarantees are absolute. They do not depend on the absence of hash collisions, or counters not rolling over, etc.
[0041] Small pieces, loosely coupled, no dark corners: as many semantically independent aspects of a Hard Object as possible are mapped to independent mechanisms, and these mechanisms are metadata localized with the code or data they manage. Doing this makes features locally and orthogonally configurable.
[0042] A Hard Object is a toolbox, not a straight jacket. A Hard Object protects you from others, but not from itself: the unit of trust is the module, so that within a module the author turns off all access protection to his or her own data. In this way, Hard Objects ensure that any program implementable on a standard general-purpose machine can also be implemented on a machine enhanced with Hard Objects.
[0043] When in doubt, zero: Hard Object attempts to name metadata such that the safest value is zero / false. For some parameters of Hard Objects, it is currently much easier to let the default C behavior be the default Hard Object setting. For example, by default Hard Objects configure pointers to have a set writable-target-flag, i.e. a pointer to a non-const, rather than the safer option of a clear writable-target-flag, a pointer to a const. However, Hard Objects can ultimately be modified so that these parameters default to safer configurations in hardware, but still give the C default behavior in software by making compiler changes, or source-to-source transformations automatically add explicit Hard-Object calls in client software to do so.
[0044] Simple is not minimal, simple is well-factored: Hard Objects restore causal locality to software. From a correctness perspective, given these primitives, Hard Objects factor out the correctness proofs of programs, Checks that are easily done in software should be done in software. Checks that are easily done in hardware should be done in hardware.
[0045] Hard Objects refactor the software and hardware responsibilities in a way that removes far more complexity from the software than it adds to the hardware. This removed complexity is significant, and it is complexity that means there is no answer to the undecidability of the Halting Problem. The premise of Hard Objects is that the resulting whole is much simpler than the current organization of software and hardware; that is, the simplicity is not minimal, but the simplicity is well-factored. Hard Objects provide properties very similar to what Java® attempted to provide, just in a lightweight / decomposed way that works in the kernel, allowing for a smooth path to adoption for C and C++ programmers. Hard Objects is what Java® should have been.
[0046] Software Locality Semantics Imposed by Hard Objects: These are the semantic properties imposed by Hard Objects in terms familiar to C programmers: All memory pages are marked as either data or text (code). Some operations are "dangerous" (risky operations) in the sense that they are powerful enough to subvert the guarantees imposed by Hard Objects, modify metadata, and store transient data in heap / global memory. Such dangerous operations are intended to be used only by trusted system code. Some functions are labeled "dangerous", meaning that Hard Objects allow them to perform dangerous operations.
[0047] Hard Object constraints are conjunctive: each aspect of a Hard Object imposes constraints on programmatic actions, and all of the relevant constraints must be satisfied for an action to be allowed. (More flexible systems can easily be built by simply being more relaxed about this requirement, but such systems may be annoying to users.) Throughout this detailed description, phrases like "check whether P(x)" or "x must be P" (where P(x) is some predicate) mean "if P(x) does not hold, the Hard Object system will raise a fault to a trusted system handler, which will get all the details of the fault."
[0048] Each flow chart diagram demonstrates only one aspect of the Hard Object being operated / enforced, but a given instruction or computer operation is operated / enforced by many aspects of the Hard Object. If someone were to try to create a diagram that showed, for a given instruction or operation, all of the Hard Object aspects that are checked when the instruction or operation is performed, the diagram would be so complex that it would be unreadable, would not fit on a single page, and no benefit would be gained from looking at it.
[0049] So instead, the drawings depict how some of the checks for one aspect of a Hard Object are performed. However, this means that a single instruction or operation may have multiple aspects of a Hard Object to check, these multiple aspects may be depicted in multiple drawings, and there must be some way to combine the results from the multiple checks depicted for a single instruction or operation. This is depicted visually across many diagrams, but when a given instruction or operation is executed, given all of the associated checks of the various Hard Object aspects depicted in multiple diagrams, if any of the associated diagrams follow a control path that ends in a "fault", the instruction or operation will cause a fault, and the instruction or operation will only be allowed if all of the associated diagrams follow a control path that ends in some form of "allow". Furthermore, the processing performed for even part of one aspect is too complex to be illustrated in a single diagram. Because there may be multiple parts to the check of a single Hard Object aspect, the control path of some flow charts ends in a box labeled "Not relevant, allow." This does not mean that the aspect passed, but rather that the check, even for this aspect, may be illustrated in a different flow chart, and that this flow chart is not relevant to the situation that led to a given end box labeled as such. Furthermore, Hard Objects do not always cause failures. Some control paths end with "allow because procedure is performed," but even though the Hard Object operation did not cause a failure, such control paths still result in changes to Hard Object metadata. Even in these situations, other drawings of the Hard Object may still operate to change other metadata or cause a failure. Furthermore, if a check for a particular aspect of a Hard Object has branches in one direction or another, various checks are performed for each branch; sometimes, a branch is simply omitted, and only one path or the other is provided in the diagram; the entire process may be obtained only by combining multiple diagrams. For example, Figure 32 shows calculating an absolute sub-object ID given some input 3209, and Figure 33 shows looking up an absolute sub-object ID in a table given some other input 3309. Which of these diagrams is used depends on what kind of input is available in the context in which the absolute sub-object ID is desired. The choice is implied but not shown in the diagram. The operation in a real machine would be a combination of these two diagrams and a step that selects one of the two diagrams based simply on which diagram operates on the available input. Again, without drawing the diagrams in this manner, they would become unreadably complex.
[0050] When discussing Hard Objects, this detailed description often refers to "pages" of memory, by which this detailed description means the standard prior art of partitioning memory into pages (such as in systems using prior art virtual memory). However, in this detailed description, pages are used only to annotate ranges of memory with metadata, and any other system capable of annotating ranges of memory with metadata could conceivably do the same job and therefore could be used in conjunction with Hard Objects. Thus, by "page," this detailed description means any mechanism for annotating ranges of memory addresses with metadata.
[0051] When describing hard objects, this detailed description often refers to “registers,” by which we mean the standard prior art practice of collecting bits of state in a CPU and naming them so that they may sometimes be thought of as a single noun / object or operated on. This detailed description refers to two types of registers: registers that are part of the CPU that a hard object observes / intercepts (“CPU registers”), and registers that are added by a hard object (“HO registers”). While some CPU designs have register-oriented instructions and use registers for input and output of most operations, such as prior art RISC-V®64 systems (“register machines”), other CPU designs may organize computations variously and do not necessarily use registers as an organizational paradigm for passing data around, such as pushing and popping data onto / from a stack (“stack machines”), or flowing the output of one operation directly to another, or some other paradigm.
[0052] Hard Objects annotate user text and data with metadata, and it is these annotations and properties that are used to enforce the heart of the Hard Object system. This detailed description refers to embodiments that are natural to register machines, such as prior art RISC-V®64 machines, and therefore uses the terminology of register machines (specifically RISC-V®64). That said, any other system that allows for the annotation of Hard Object metadata to associated data and text could conceivably be made to work with Hard Objects. Thus, by "register," this detailed description means any way of collecting data and treating it as a unit of data, text, or metadata, regardless of whether the collection of data is literally articulated as a prior art register mechanism, as long as it acts in the function of providing semantics as a unit when its value is needed in a computation that requires semantics.
[0053] control Calls and returns: (function) calls may only target the beginning of a function. Returns must target the instruction following the corresponding call (unless the called function has a critical system capability that causes setjmp / longjmp to work). There is no other way to transfer control to another module except via interrupts / exceptions. Cross-module jumps are presumed to be calls and work only if they target the beginning of a public function (hence tail calls are possible).
[0054] Inevitable Dynamic Checks: Assume that static analysis cannot prove a fact on which a program's correctness or security is based. Such analysis might insert a dynamic check for a relevant fact at a relevant point in the code. What if the program jumps past this dynamic check, defeating it? Static analysis can check every static control flow transfer to prevent this from happening, but it is possible for a program to jump through a dynamic control flow transfer, i.e., a pointer whose value is unknown at static time. Any current dynamic control flow transfers would only move to the beginning of the function, thus avoiding bypassing the inserted dynamic check. A future extension could allow dynamic control flow transfers into the middle of a function and require the use of formal text pointers (see references below), which are restricted to critical system code and on which pointer arithmetic is not allowed. Static analysis could then generate instructions to trusted system code to suppress dynamic control flow transfers, thereby preventing bypassing such dynamic checks.
[0055] space Object: Contiguous regions of data pages may be annotated as objects. Objects do not intentionally overlap. For C code, objects are used to model global variables; memory is reserved by malloc(), and automatic variables reside on the stack. A heap / global pointer is associated with an individual object. It can point anywhere within that object or just off its edge. Pointer arithmetic that points elsewhere can fail. Dereferences / memory accesses (loads / stores) that do not point within the object region fail. In contrast, the prior art Valgrind system [valgrind] easily misses errors that hard objects catch, such as leaving the edge of one global variable and going to another (at least in Valgrind's default configuration).
[0056] Subobjects: Objects may be overlaid with subobjects. Pointers may be annotated with subobjects (e.g., using some form of subobject ID that can select the subobject from the collection of subobjects annotated on the object). Subobjects consist of spatial boundaries on an object that constrain access of a pointer annotated with one of them to only a portion of the object (unless they are illegal subobjects, in which case the constraint extends to the entire object). If access is attempted from outside the constraint boundary, the hard object will issue a fault. Subobjects may be nested or overlapping. Subobjects can be used to model members of an object's type. When modeling the C language, one embodiment is to generate a subobject tree that mirrors the structure of the C type tree for the object's type; thus, a subobject is generated recursively for each struct, union, or array and each member. This subobject tree may be refined to have more parts than a C tree, such as generating two subobjects for an array: an array and an iterator. A subobject iterator through the array does not have the type of the array, but has the type of the elements of the array, However, it is a subobject that allows access to the entire array, rather than restricting access to just one element of the array.
[0057] Subobject iterators are designated by annotating the subobject with an iterator flag that is set. Heap / global pointers are associated with a specific subobject, but by initialization default to the entire object. A subobject reference may be obtained by narrowing an object or subobject pointer so that it is limited to the scope of that subobject. As with objects, a subobject pointer may point within the subobject or just leave its end, and will be a fault if it goes outside its scope, and will be a fault if it is dereferenced outside its scope.
[0058] Stack Boundaries: The stack is bounded by the stack-base-ptr and stack-limit-ptr hard object registers. The stack pointer is a short-lived Abs-Ptr that points into the stack. In one embodiment, heap / global Abs-Ptrs cannot be made to point into the stack using pointer arithmetic, as this would put them outside of their page-class-id range. The hard object maintains a frame pointer on calls and returns that mirrors the user frame pointer, but cannot be destroyed.
[0059] Access to the stack must be through a formal pointer. The only pointers allowed in the stack for this purpose are (1) a copy of the original stack pointer (usually modified by pointer arithmetic) or (2) a stack-obj-pointer (see below). A hard object system maintains its own frame pointer, the hard-object frame pointer / frame-pointer (or shadow-frame-pointer), which cannot be written by user code. If a stack pointer attempts to access the stack and the access is above the hard-object frame pointer, the access is flagged as invalid. A stack-obj-pointer can allow access above the hard-object frame pointer (provided it reaches the callee). A stack-obj-pointer can only be created using a special hard-object instruction that "narrows" the stack pointer to the stack bounds. This is the instruction used when the compiler encounters the expression "&x" which takes the address of the stack variable "x". Stack objects have space bounds. The bounds of a stack object are always immediately annotated to the stack-obj-pointer, i.e., no table indirection is needed to find them. A callee cannot access the frame of its caller unless the caller explicitly narrows the stack pointer to a particular one of its stack objects and then passes a pointer to that stack object to the callee.
[0060] time Register calling conventions and callee-save safety: CPUs (such as RISC-V®) often mark each user general-purpose register as callee-save or caller-save. During a function call, the hardware clears the ref-flag of the caller-save register. Hard objects require call-return pairs to preserve the integrity and privacy of caller-save and return-address registers accessed by the callee by requiring their values to be saved (to the stack) before first use, protected (to prevent corruption while being saved), and retrieved (from the stack) after last use and before the function return. During a function call, the hardware automaton ensures that callee-save registers (including the return-address (ra) register) are handled correctly. When callee-save register R1 is accessed, the hardware ensures that: -R1 is saved first. · R1 is reclaimed later, before the callee returns (except in cases where the register could be overwritten without being saved and then the function never returns). R1 is retrieved from the same location where it was saved. The stack location where R1 is saved cannot be corrupted. · The ref-flag in R1 is cleared to prevent any formal pointers from leaking from the caller to the callee.
[0061] Furthermore, the same mechanism saves (using the save-callee-save-reg-state operator) and recovers (using the restore-callee-save-reg-state operator) and protects the automaton state itself by treating its own state as another callee-save register while being saved.
[0062] Register privacy-after-call and privacy-after-return: Functions may zero their caller-save registers before calling and their non-return-value / non-callee-save registers before returning, but doing so is expensive. The hard object annotates each user integer register and floating-point register with a written-flag using special hard object registers for it: the int-register-written-flags (int-register-written-flag-s array) and float-register-written-flags (float-register-written-flag-s array) registers, respectively. Each integer register and floating-point register has a unique register-id. The hard object clears this register written-flag (and the parallel / corresponding ref-flag) if the value of a given register is not expected to propagate to the next function that receives control on a call or return. For more information, see Upon call, the register written-flag (and parallel / corresponding ref-flag) on caller-save registers with no arguments is cleared. A program can mark registers for which arguments are not cleared by various means, for example by setting the special hard object num-int-args-in-registers (or num-float-args-in-registers) register before writing the number of registers using the special put-num-int-args-in-registers (or put-num-float-args-in-registers) instruction / operator (which takes a num-args parameter), or by annotating the called function's metadata with the number of arguments, or by annotating the register-ok-to-call-flags register (and similarly for floating-point argument registers) with ok-to-call-flag. When saving a callee-save register to the stack, clear the written-flag (and ref-flag) of the saved register so that the callee cannot subsequently read it. On return, clear the register written-flag (and ref-flag) for registers that are not callee-save registers and are not marked as return values. As with calls, a program can mark return registers by various means, for example by setting the special hard object num-int-ret-vals-in-registers register before using a special instruction (or simply reusing num-int-args-in-registers), or by annotating the number of return values in the returning function's metadata, or by annotating a register with the ok-to-return-flag of the register-ok-to-return-flags (ok-to-return-flag-s array) register (and similarly for floating-point return registers).
[0063] In one embodiment, if the ok-to-return-flag is not set in a caller-save register, the hard object clears the ref-flag and written-flag for that register upon function return, thereby making the register's formal pointer a useless integer and making the register unreadable. The hard object provides a special instruction, the set-ok-to-return-flag instruction / operator, that a program can use to set the ok-to-return-flag for a register; the instruction sets the flag only if the hard object allows the register's value to be returned. The following checks are made on a register value to make its return ok (note that a register contains a formal value when its ref-flag is set): If the register value is a short-lived pointer, its return is not ok. If the register value is a formal function pointer, then if it has the durable flag, the return is ok. If the register value is a formal return pointer, the return is not ok. If a register value is of the form stack-obj-pointer, it may be returned if the frame pointer annotated to it (its stack time) is strictly greater than the current hard object frame pointer of the current frame (being returned), i.e., the stack object pointed to by that stack-obj-pointer will outlive the current frame (being returned). · Other formal pointers and informal values (without ref-flag) are ok to return.
[0064] A hard object sets a register's written-flag when the register is written. If a register with written-flag cleared is read, the result depends on the read-unwritten-int-reg-policy or read-unwritten-float-reg-policy, respectively. This policy can be (1) allow, i.e., simply read a normal word, or (2) read-zero, i.e., read and return zero regardless of the actual memory value (or, more generally, return an unwritten-indicator-datum to indicate that the memory read was not written; one embodiment uses zero for the unwritten-indicator-datum), or (3) fault, i.e., a failure.
[0065] A hard object's function register data must not leak to subsequent functions that gain control on call or return. A similar policy can be followed for a context-switch into the kernel, allowing propagation of values from registers intended for kernel use and those not.
[0066] Preventing stack use-after-free: A program may create formal pointers, stack-obj-pointers, that point to stack objects using special hard-object instructions. Such stack-obj-pointers contain an encoding (or annotation) of the frame pointer of the frame of the object they point to. The hard object uses its frame-pointer portion of the stack-obj-pointer as a stack time formal. The hard object prevents such formal stack-obj-pointers from spilling out of the frame of the object they point to by: stack-obj-pointers may not be stored in heap / global memory (except by code with dangerous powers). A hard object prevents storing stack-obj-pointers to other stack objects or frames that will outlive the frame of the object they point to. Specifically, when storing a stack-obj-pointer, the only way to store it in a stack of a frame that will outlive the current frame is to store it through a second stack-obj-pointer. Both of those stack object pointers have frame pointers annotated that the hard object uses as a kind of stack pointer. That way, the hard object does not allow this store unless the second stack-obj-pointer points to an object that will not outlive the object pointed to by the first stack-obj-pointer being stored. Again, since both stack-obj-pointers have frame-pointer annotations, this property can be checked by just comparing the two frame-pointer annotations of the two stack-obj-pointers at the time of the store. stack-obj-pointers may not be returned by a function if the frame to which they are returned outlives the stack object pointed to by the stack-obj-pointer. That is, for any caller-save register that does not have a ref-flag that is cleared on return, the register must have the ok-to-return-flag set. If ok-to-return-flag is set on a register containing stack-obj-pointer, the frame pointer annotated to that stack-obj-pointer is checked against the current hard object frame pointer, and if the terminating frame is strictly less alive than the stack-obj-pointer frame pointer (hence the frame being returned will be at least as long-lived as the stack object pointed to by stack-obj-pointer, assuming all non-leaf stack frames are non-empty, as hard objects enforce at function call time), then ok-to-return-flag is set and stack-obj-pointer may be returned; otherwise, it may not. Doing this effectively ensures that if a function attempts to return a pointer to a frame that has a longer lifespan than the object pointed to by stack-obj-pointer (it is a stack use-after-free), then stack-obj-pointer cannot actually be returned. Again, for caller-save registers that do not have the ok-to-return-flag set, the hard object clears the register's ref-flag upon function return, thereby making the stack-obj-pointer a useless integer, and clears the written-flag, making the register unreadable.
[0067] Therefore, it is not possible to obtain a stale stack-obj-pointer (which points to the object of a frame that has been freed by the function return). In this way, hard objects ensure that there is no use-after-free for stack objects. This is the same demonstration as in the "Stack Object Reference Propagation" section below.
[0068] Stack privacy-after-free: To prevent callers from reading anything left in their stack frame, functions could zero their stack before returning, but doing so is expensive because it requires many more writes. The design perspective for hard objects is that this stack memory will be overwritten anyway, so why not use hardware to guarantee that? For this purpose, hard objects provide a stack-floor register. The stack floor is raised to the hard object frame pointer (of the callee) on return (from the callee). The stack-floor may only be lowered by a write if the sum of the target address of the write and the data width of the write (write-top) is equal to the (current) stack-floor, thereby lowering the stack-floor to the target address of the write. Note that this write has the effect of clearing any previous data on the stack in the new memory made accessible by lowering the stack-floor. That is the main point.
[0069] Doing this ensures that the callee's stack data is not leaked to the caller or the next callee. The compiler may sometimes generate code that skips some stack locations, so when working with hard objects, its behavior must be changed to not miss locations unless the stack-floor-init-flags register is used. This register holds written-flags for the window of registers below the stack-floor where faults were written. User code may programmatically raise (but not lower) the stack-floor using special hard-object operators, as long as it does not raise it above the hard-object frame pointer.
[0070] Prevent heap / global use-after-free: Heap / global objects have a version (i.e., refable-version), and pointers to heap / global objects have a time (i.e., time-address). When accessing (reading or writing) a data object through a heap / global pointer / reference (dereferencing), the time-address of the pointer / reference must match the object's version, or a hard object fault occurs. When a heap object is deallocated, its version is incremented (as is the case when a global is unloaded into a dynamically loaded executable and linkable format (ELF) library). Therefore, a use-after-free on heap / global data may not occur unless the allocator reuses an object version that is still in use as the time-address of a valid reference.
[0071] Heap / Global Object Version Collection: Hard objects require collection of old references (when reference time does not equal the object version). Hard objects do not require garbage collection. Garbage collectors are simply impossible to make work well. Hertz and Berger [hertz-berger-2005] estimate that Java garbage collection requires three to five times more memory to perform as well as a non-garbage-collected program. As a result, version collectors outperform garbage collectors in several algorithmically fundamental ways. Unlike garbage collection, version collection is easily parallelized, adapts to concurrent memory hierarchies, and, if run frequently enough, never leaves dead memory in the client program (whereas garbage collection requires dead memory before it can do anything useful). That is, by separating the use-after-free problem from the garbage problem, hard objects radically improve this situation.
[0072] Heap / Global Privacy-After-Free: Hard objects currently do not guarantee that the contents of a heap / global object will not leak to the next user of that object through free-then-alloc object reuse. Obviously, a module could zero the object before free()ing it, or the allocator could do this, but this is known to be time-consuming. A hard object's format pointer cannot leak through free-then-alloc object reuse. To do this, all that is required is to clear the object's memory ref-flag when the object is deallocated. Given that metadata flags for multiple contiguous data words are all stored in a single metadata word, this is orders of magnitude faster than zeroing the data. Hard objects have a mechanism to annotate memory with a written-flag, which can be similarly cleared when the object is deallocated and is set in the machine code when it is written to memory (note that if part of the machine code is written, the remaining machine code must also be zeroed if the written-flag is cleared before writing). If memory with written-flag cleared is read, the result depends on the read-unwritten-mem-policy, which can be "allow", which reads the word normally, "read-zero", which returns zero whatever the actual memory value, or "fault", which fails. Given that written-flag is similar to ref-flag, if ref-flag is cleared, clearing written-flag can be done in the same path as ref-flag, so metadata flags annotated to the same memory take no additional time.
[0073] Modularity Module Owner and Module Ownable: A module is represented as a module-id (a bit string). We interpret the module-id as a path from root to leaf in a complete binary tree. We select a subset of the internal nodes of this binary tree to be a module, and the leaves of that subtree are called its submodules. Two internal module nodes are not allowed, where one is an ancestor of the other. Code is annotated with an internal node of this tree called mod-owner, which contains the module-id and module-owner-suffix-length. Heap / global data is annotated with a leaf of this tree called mod-ownable, which contains the module-id. For code to access data, the mod-ownable of that data must be a leaf in the subtree under the internal code of that code's mod-owner. Similarly, to allow access, the module-id of the code's mod-owner must match the module-id of the data's mod-ownable, except for (i.e., ignoring) the rightmost length bits of the code's mod-owner's module-owner-suffix-length. A module is a unit of trust. Modules cannot touch each other's data unless the data is marked public. A module-owner's submodules are simply a collection of mod-ownable module-ids (all of which are mod-ownable) that differ from the module-owner's module-id only in the rightmost length bits of the module-owner-suffix-length. That is, one mod-owner can have many mod-ownables, which are called the mod-owner's submodules. More details are provided below.
[0074] Public and Private Data: Data can be annotated as public, and therefore access to such public data by code in other modules is not hindered by the hard object modularity aspect. Data can be annotated with the public-flag at both the machine code (RISC-V®: double word) granularity and the object granularity. Access to public data can also be constrained in other ways, such as requiring unforgeable pointers, equivalent to capabilities provided by the object's constructor. Stack data is protected in a different way; a function may pass a pointer to one of its stack objects to another function in another module. Modularity also has the effect of constraining who can globally modify metadata about data, even if it is public by modularity. In particular, this constraint prevents a module from deleting another module's object.
[0075] Public and Private Pointer Targets: Pointers have a public-target-flag that, when cleared, does not allow access to another module's data, even if that data is public.
[0076] Public and Private Functions: Functions are annotated as public or private. Inter-module calls may only target public function heads. Inter-module control flow is therefore limited to public function calls and their corresponding returns (all functions of a module may access that module's data; the public / private nature of a module concerns who can call it, not what data it can access).
[0077] Submodules: Submodules are useful for modules to use as class-ids to distinguish between different classes within that module, in a manner similar to C++ Runtime Type Identifiers (RTTI). In C++, temporarily thinking in terms of submodules, a module with two classes Foo and Bar can easily prevent methods on class Foo from operating on a pointer to an instance of class Bar by inserting a check at the beginning of each method that the method expects the submodule annotated on the object pointed to by the implicit parameter of the "this" pointer.
[0078] Module Groups: Module groups allow a main program module group to exclude a collection of other untrusted modules, such as dynamically loaded untrusted ELFs downloaded over the Internet, from successfully accessing mutually trusted modules. Such excluded modules can be prevented from creating or using capabilities, even in a capability-based system. Modules from other groups are thereby automatically sandboxed without any further effort on the part of the main program. Data objects can be annotated with the following metadata: may-read-suff-len: When a read from instruction at a text address targets a data address, the hard object will fault if the mod-owner-id of the text address does not match the mod-owner-id of the mod-ownable of the target data address except for the rightmost length bits of the may-read-suff-len of the target data address. may-write-suff-len: When a write from an instruction at a text address targets a data address, the hard object will fault if the mod-owner-id of the text address does not match the mod-owner-id of the mod-ownable of the target data address except for the rightmost length bits of the may-write-suff-len of the target data address. may-make-ref-suff-len: When a data pointer / data reference consists of the target data address of an instruction at an instruction text address, the hard object will fault if the mod-owner-id of the instruction text address does not match the mod-owner-id of the mod-ownable of the target data address except for the rightmost length bits of the target data address's may-make-ref-suff-len.
[0079] Functions can be annotated with the following metadata: may-call-suff-len: When a call from an instruction at a text address in a calling function targets the text address of a called function, if the mod-owner-id of the calling function does not match the mod-owner-id of the called function except for the rightmost length bits of the called function's may-call-suff-len, the hard object will fault. · may-make-ref-suff-len: When a function pointer / function reference consists of the target text address of an instruction at the instruction text address, the hard object will fault if the mod-owner-id of the instruction text address does not match the mod-owner-id of the target text address except for the rightmost length bits of may-make-ref-suff-len of the called function.
[0080] Caller-mod-owner register: When a function call is made, the caller-mod-owner register is set to the mod-owner annotated on the code making the call. When the function returns, caller-mod-owner is set to nobody mod-owner. That is, at function start, the calling module is available to the callee as the value of the caller-mod-owner register. Using caller-mod-owner as a kind of authentication raises the Confused Deputy Problem, but it is still useful for additional authentication in certain situations.
[0081] Integrity Flags: Considering for a moment in C++, the typical way to establish and maintain correctness of data structures is to Wrap it in a class, Establish its invariants in the constructor, Put invariants at the beginning of each method, ·Make sure to put the invariants again at the end of each method.
[0082] However, it is important to know whether the invariants have been fully established or if the object's state temporarily does not satisfy the invariants. A module may transfer ownership of an object to another module. This enables a Trojan Horse attack [homer-8th-cent-bc] of module M1 against module M2, as follows: 1. Module M1 creates a subtle deception object O1. 2. M1 transfers its ownership to module M2. 3. M1 invokes the method M2::foo() on M2, passing as its pointer a pointer to O1. 4. Trick M2::foo() into trusting O1, into thinking that O1 is one of its own objects, and by some subtle deception somehow cause it to fail.
[0083] To counter this attack, hard objects annotate each heap / global object with an integrity-flag. If this integrity flag is cleared, normal memory-access (load / store) instructions may not access the object; instead, only special non-integrity memory-access (load / store) instructions / operations may do so (never by accident). The owner of an object can transfer ownership of that object to another module using a hard object ownership transfer instruction. When object ownership is transferred, the integrity-flag is also cleared. Only the (new) owner may set the integrity flag again (as enabled by sane code), and is presumed to do so only after verifying that the invariants are valid for the object.
[0084] The owner may wish to keep the integrity-flag cleared until initialization / construction is finished, i.e., until the invariants are guaranteed to be valid.
[0085] Reference: A pointer may be annotated with a ref-flag, making it a formal pointer known as a "reference" or "ref". A pointer may be embedded or annotated with a Ptr-Kind-Enum and possibly an Abs-Ptr-Kind-Encoding to indicate what kind of structured pointer it is. There are several kinds of structured pointers (each with multiple metadata fields within it). Absolute heap / global data pointers and stack pointers, with subtypes (a) persistent and (b) temporary (including stack pointers) Stack object pointer (further qualified from stack pointer) Function pointer / forward text pointer Return pointer Otherwise, it is an unstructured-lo or unstructured-hi pointer. That is, sometimes this detailed description describes a kind of structured pointer, or a Ptr-Kind-Enum of structured pointers that is unstructured-lo or unstructured-hi, meaning that the pointer structure is null, and we call such pointers "raw" or "unstructured pointers", even though they are a kind of structured pointer; conversely, we call other kinds of structured pointers "strictly structured" (or, perhaps confusingly, simply "structured") when we want to emphasize the desirability of the contrast.
[0086] The encoding of a structured pointer cannot be forged by user code if the pointer is a formal pointer. In one embodiment, hard objects do not allow raw pointers (unstructured-lo or unstructured-hi) to be formal pointers, so when the phrase "formal pointer" is used in the above detailed description without mentioning whether the pointer is strictly structured or raw, it implies that the formal pointer is strictly structured.
[0087] Absolute heap / global references: Heap / global objects must be accessed through absolute heap / global style pointers / references (unless annotated with the refable-informally-targetable-flag).
[0088] Stacks and stack-object references: A stack pointer is a (short-lived) absolute pointer. The stack must be accessed through a formal pointer like stack-pointer. Recall that stack objects and stack-object pointers are created by qualifying the stack pointer. A stack object above a hard object's frame pointer must be accessed through a stack object reference. Note that this assumes that the stack is lowered, as is done in many prior art systems, and that "above the frame pointer" means the stack frame of the calling function being stopped in.
[0089] Function pointers / forward text pointers / function capability: Control flow transfers that are neither program-counter-relative (PC-relative) increments nor PC-relative jumps or branches, but are forwards (i.e., not returns), must be made through formal forward text pointers. Combined with the constraints on the creation of such formal pointers (or, more precisely, the constraints on their ref-flag annotations), this mechanism provides function capability. Function pointers / forward text pointers can be annotated with the function-body-target-flag. When it is set, this formal pointer may be used to call into functions; when it is cleared, it must be used to call only to the beginning of functions. When jump tables are not used, function pointers that call other than the beginning of a function are needed only for very large intra-function jumps, when the distance cannot be expressed as a PC-relative offset on some architectures. Function pointers / forward text pointers can be annotated with the cross-module-target-flag. If this is set, this format pointer may be used to call across modules; if it is cleared, it must only be used for calls within a module.
[0090] Return-pointers / Ret-and-frame pointers: The control flow transfer that is a function return must occur through a formal return pointer; because such a pointer contains information about the frame to which the function returns, the return pointer is also called a ret-and-frame. Hard objects maintain an incorruptible hard-object frame-pointer (or shadow-frame-pointer) that is independent of the program's user frame pointer. This must be recovered on return, and for that purpose, the ret-and-frame pointer contains an encoding of the frame pointer of the frame to which it returns, so that the hard-object frame pointer can be recovered on return. Hard objects maintain a current-function-start, i.e., a pointer to the start of the current function. This must be recovered on return, so for that purpose, the ret-and-frame pointer contains an encoding of the current-function-start of the function to which it returns.
[0091] Reference Creation and Propagation: The intent is to constrain reference creation and propagation so that they always point to a live, real object of the target class. create: Heap / global references may only be created by the allocator at allocation time (unless the object is constructed in another way). A stack reference may only be created by qualifying a stack pointer to point to the current frame. Other types of references may only be created by the Hazardous System Code. propagation: Reference / format pointers may only be propagated by move instructions, which must move data the size of a machine word the size of the entire pointer (moving part of a machine word does not copy the ref-flag, so in one embodiment format pointers cannot be moved partially, i.e., the move must be pointer atomic). Any other operation on a reference will cause it to lose its referentiality.
[0092] Stack Object Reference Propagation: A stack object reference cannot be separated from the liveness-related aspects of the object it points to. A stack object reference may be returned from a function, provided that the return frame does not outlive the object it points to. A stack object reference may be saved on the stack, provided that it is saved in a frame that does not outlive the frame of the object it points to. Stack object references do not have to be saved to heap / global data except by dangerous code (enabling setjmp / longjmp).
[0093] Heap / Global References and Formal Function Pointer Propagation: Short-lived References: Functions and heap / global pointers can be annotated with the durable-flag. When this flag is set, the pointer is "durable", when it is cleared, the pointer is "short-lived". A short-lived function or data reference does not have to be saved to heap / global data or returned from the function. A durable heap / global reference may be reduced to a short-lived reference, but the reverse is not possible (without using dangerous powers). Short-lived references allow a client to pass a short-lived capability to a library and know that when the library returns it, the library has not stashed a copy of that capability for later use.
[0094] Use of both formal and informal pointers: Objects can be annotated with multiple metadata flags that give multiple modes for who may annotate references to the now-allocated object, who may change the metadata, etc. A module author may take advantage of this flexibility by configuring objects to be accessible only by the module's code, while allowing code within that module to create formal pointers from informal (int) pointers as needed. Using this technique, a programmer could create the following XOR-linked list: Doubly linked lists save space by XORing forward and backward pointers. This operation is not allowed for format pointers. And since the data is protected from other modules, it still has local proof of correctness.
[0095] This technique passes, not fails, when it demonstrates a forced implementation of the boundary, which, in contrast to the case of hard objects, cannot be done in either a fully formal language like Java or a fully informal language like C. Hard objects are a platform on which we can speak in both formal and informal languages, just as we do with natural human languages.
[0096] Mutability: Making data immutable greatly improves our ability to reason about the semantics of a program. Once an immutable object has meaning (initialized / constructed), that meaning never changes. Hard objects make it possible to annotate machine code and (semi-redundant) objects with a writable-flag and make them immutable (by clearing that writable-flag). Hard objects also make it possible to annotate heap / global / stack object pointers with a writable-target-flag to give mutable objects an immutable face.
[0097] An embodiment that enforces the semantic properties: This section presents an embodiment that enforces the semantic properties of Hard Objects presented above. In this detailed description, when an element "has," "annotates," or "is associated with" another element, there are many ways to implement the annotation; this section presents one such embodiment. In this detailed description, when we say "the Hard Object performs a check / assertion," we imply that the Hard Object will fail if the check or assertion fails (determines something other than true). Since Hard Object checks / constraints / conditions / invariants are connective, and any operation constrained by multiple aspects must satisfy all of them to be allowed, this detailed description states that "operation X is allowed if condition Y" implicitly means that operation X is allowed only if operation X also satisfies all other Hard Object conditions of all other aspects of the Hard Object. That is, if the necessary checks / conditions / constraints / invariants related to the operation are not met, the Hard Object will fail (or just "the Hard Object will fail").
[0098] When this detailed description refers to a noun "annotating" another noun, it means that the nouns are somehow related, but the mechanism of this relationship is intentionally left unspecified so that it can be chosen independently as a matter of individual implementation. To say that a noun has or is composed of a field / member / part actually means that the noun is somehow annotated with that field / member / part. Furthermore, any annotation instantiation, even one that it has / is composed of / is a part of, need not be embodied or implemented in a way that represents any mechanical connection or location; specifically, the association need not represent spatial location (embedded or other forms of memory address location or physical wire location), temporal location (computed at the same time or nearby in time or at a temporal location), or any other form of mechanical connection or location. Throughout this detailed description, "put a:= b" means taking the value of register / field / annotation b and putting that value into register / field / annotation a. In this detailed description, we tend to use RISC-V-64 terminology (see [RISCV]). One quirk of this terminology is calling a pointer / machine word a "double-word" or "dword" (where a "word" is 4 bytes in RISC-V, 8 bytes in RISC-V).
[0099] Terminology: Any instruction that accesses memory is a memory access instruction, including load and store instructions. Load instructions are sometimes called read instructions. Store instructions are sometimes called write instructions. The register that gets the value for a read from memory is sometimes called the load-destination register. The register that provides the value for a store to memory is sometimes called the store-source register. Forward references will probably still be present, even if you try to minimize them.
[0100] Control: The types of control flow transfers (types of control-transfer instructions) in the RISC-V (registered trademark) architecture are as follows (other architectures may have subtle differences from this mechanism, but such differences are not fundamentally important, so in the embodiment of this invention, the RISC-V (registered trademark) mechanism is used): fallthrough: increment to the next instruction, jump-or-branch-static: A jump / branch whose target is known at rest, often expressed as a literal or literal offset from the program counter, ·jump-dynamic: Jump to the value in the register. call: stores the next address (where return will return to) in the ra (return address) register or on the stack (or similar) or somewhere else that uses return, jump, · return: Return to the address produced by the call to the address after the call command.
[0101] Some prior art instruction set architectures have jump-register or jump-and-link-register instructions that, depending on their configuration, can be used to implement jump-dynamic, call, or return. In Hard Object, both call and dynamic-jump have two additional configuration aspects: A module change aspect, with possible values of (a) mod-same: must be called in the same module, and (b) mod-cross: may be called across multiple modules; A function part target aspect with the following possible values: (a) to-func-top: must be called only on the function top, (b) to-func-body: can be called anywhere within the function.
[0102] Control flow constraints may be enforced by various embodiments: Instructions that initiate control flow (branch / jump / call / return) perform checks. An instruction that starts a control flow sets the control-flow-kind Hard Object register. At the start of each instruction, the Hard Object reads the value of the control-flow-kind register (placed there by previous instructions) and performs some processing depending on that value (such as performing some checks) before successfully executing the instruction. The checks described below can be performed in the above embodiment as well.
[0103] Useful information to include in control-flow-kind is: Whether the control-flow transfer is a fall-through to the next instruction, a static jump / branch, a dynamic forward jump (via a function pointer), or a return (via ret-and-frame), Whether control-flow transitions are allowed into the body of the function or can only go to the Top, · Whether control-flow transitions are allowed across modules or only into the same module.
[0104] The function then binds: ·Hard Objects maintain the Function-Metadata of the currently executing function in a local register. Once that Fiction-Metadata is loaded, the function start is known both because (a) control is transferred right to the top of that function (and therefore the address of the top of that function is immediately available, just as if it were used to jump to the top of that function), and (b) control is returned by return via the return pointer to the function, which contains enough information to compute the top of the function, along with the Text-Page-Metadata of the return target address (because it needs to look up the Fiction-Metadata at that time). · The function end can be computed from the functions top and length, which can be obtained from Function-Metadata. Store the function start in the local current-function-start register and the function end in the local current-function-end register.
[0105] Fallthrough: Absent an explicit control flow transition (not a branch, jump, call, or return), an instruction defaults to fallthrough in the control-flow-kind register. At the top of any instruction whose control-flow-kind is fallthrough, check whether the current program counter is still within the range bounded by the current-function-start and current-function-end registers. Therefore, fallthrough cannot take control away from the current function. Because fallthrough is constrained within a function and the function is entirely within one module, fallthrough cannot transfer control to another module.
[0106] Jump-or-branch-static: At the top of any instruction whose control-flow-kind is jump-or-branch-static (the previous instruction performed a static jump or branch), check whether the current program counter is still within the range bounded by the function start and end registers. Therefore, jump-or-branch-static cannot take control out of the current function. · Because jump-or-branch-static is constrained within a function, and the function is entirely within one module, jump-or-branch-static cannot transfer control to another module.
[0107] Jump-dynamic: At the top of any instruction whose control-flow-kind is jump-dynamic (the previous instruction was a dynamic jump, i.e., a jump through a register), (1) check whether the new program counter (the target of the jump) is at the top of a function by checking the program counter's func-top-flag. If the program counter is at the top of a function, then this jump is actually a call (tail call / sibling call), so treat it as going to the "On call" paragraph below. Otherwise, this jump is not a call, but rather presupposes the rest of this paragraph (including the bullets). (2) Control must stay within the same function, so if the control-flow-kind annotates the transition as to-func-top (rather than to-func-body), a failure occurs. Therefore, a jump-dynamic (but not a call) cannot take control out of the current function. · Because jump-dynamic is constrained within a function, and functions are entirely within one module, jump-dynamic (but not call) cannot transfer control to another module.
[0108] When calling: As detailed elsewhere, when call constructs a return pointer, it annotates it with a function-top-offset such that, upon return, the current-function-start can be reconstructed from that function-top-offset and from the func-at-page-start annotated in the Text-Page-Metadata of the return pointer's target program counter. At the top of any instruction whose control-flow-kind is call (the previous instruction either called or dynamically jumped to the top of a function), (1) check whether the new program counter (the target of the call / jump) is at the top of the function by checking the func-top-flag of the new program counter. If the new program counter is not at the top of the function, it is a fault. (2) If the control-flow-kind annotates the transition as mod-same (not mod-cross), after reading the Function-Metadata for the new function, check whether the mod-owner-id in the Function-Metadata is the same as the mod-owner-id of the previous instruction, and if they are not, it is a fault. Obtain the metadata for the new current Function-Metadata register by retrieving it from the program counter. Maintain the stack-floor invariant: (1) check stack-floor<=new-stack-floor (assuming the stack grows), (2) let stack-floor=stack pointer, (3) revoke the sub-stack-floor-init-flags register. Enforce that the caller frame is non-empty, i.e., that the stack pointer is strictly less than the Hard Object framepointer (given that the stack grows); note that this is not checked, since leaf function frames are by definition never the caller frame during a function call. Enforce the stack-obj-floor invariant: check that the new stack pointer <= stack-obj-floor (assuming the stack grows). · (1) Clear the written-flag and ref-flag of all caller-save registers that are not marked as arguments, and (2) clear all Hard Object general-purpose (including scratch and argument) control status registers. ·Let caller-mod-owner := previous current-mod-owner and new current-mod-owner := new Function-Metadata's mod-owner. Check whether the for-this-func-flag is true, and if not, raise an error, thereby canceling the for-this-func-flag to false.
[0109] On return: The calee-save aspect of Hard Objects ensures that the value of the return register cannot be corrupted from the value originally set by the call (see calee-save mechanism below), so the return is guaranteed to target the address intended by the call. · Returns can only be made through a formal return pointer (see references below). As detailed elsewhere, reconstruct the current-function-start from the function-top-offset annotated to the return pointer, and the func-at-page-start annotated to the Text-Page-Metadata annotated to the new program counter that the return targets. · Obtain the metadata for the new current Function-Metadata register by searching the metadata from the current-function-start. · (1) Cancel the written-flag and reg-flag of all caller-save registers that are not marked as return values (and do not have the ok-to-return flag) and (2) cancel the written-flag and reg-flag of all Hard Object general-purpose (including scratch and argument) control status registers. stack-floor and stack-obj-floor invariants: (1) let caller stack-floor := Hard Object framepointer (it cannot be garbled because it is maintained by the Hard Object), (2) let stack-obj-floor := callee stack-floor, (3) maintain the sub-stack-floor-init-flags register. Maintain the Hard Object framepointer: (1) The Hard Object framepointer is encoded in the ret-and-frame style pointer generated by the call / jal / jalr instructions, (2) upon return, recover it from the (a) stack-limit-ptr and (b) framepointer-uprelto-stack-in-qword fields of the Ret-And-Frame-Ptr (return through its control): specifically, compute a new framepointer by adding (a) stack-limit-ptr and (b) framepointer-uprelto-stack-in-qwords times the number of bytes in a qword (a 16-byte RISC-V® "quad-word"). ·caller-mod-owner:=nobody-Mod-Owner. Check whether the for-this-func-flag is false, and if it is not false, raise an error, thereby making the for-this-func-flag true.
[0110] Hard Objects introduce unavoidable dynamic checks: Hard Object Hardware requires that all dynamic control flow be through formal text pointers, and if dynamic control flow is to be through formal pointers, they will be annotated with the Hard Object reg-flag, so dynamic control flow can only be done through special Hard Object instructions. Control flow transitions can only transfer control within a function, call the top of the function, or return to its address immediately after the call. (1) Calling the top of a function requires a formal pointer that can only be created by a recognized Hard Object instruction, and Hard Object enforces that such a pointer cannot be later modified. (2) Returning requires a formal return pointer that can only be created by a Hard Object-midfiled call instruction, and Hard Object enforces that such a pointer cannot be later modified; furthermore, the callee-save-reg-state mechanism treats the return address (ra) register as a callee-save register and requires that it be saved and restored correctly; furthermore, since the return address register cannot be saved to heap / global memory (except by code with dangerous exponents) or returned to (without losing its form), it cannot escape the stack frame of the callee function of a call that produces a formal return pointer. Control flow transitions that transfer control within a function may be constrained to target only statically known addresses. (1) A simple way to do this is to not even allow dynamic jumps to be annotated with to-func-body (i.e., both creating a fault and disallowing its creation); this means that control flow transitions can only call the top of a function, not anywhere in its body; recall that a call must call the top of a function, and that a return can only return to the address immediately after the function call that created them. That is, any control transfer that does not have a statically known target and is not a return, but is not to the top of a function, is a fault. The necessary modifications to client software (so that correct software does not fail) that allow it to run under this constraint are quite easy to achieve, and are accomplished with standard compilers by simply disabling jump table generation. (Additional techniques to allow for very large functions may be needed, but these are rare, and the workaround is for the programmer to simply break them up.) (2) More sophisticated methods are possible using static analysis. Statically knowing all of the targets of control flow transitions means that a trusted system can add dynamic checks to a program that the program cannot avoid. Thus, dynamic analysis of a program can be applied to imprecise refinements in software without adding additional hardware.
[0111] Space: A partitioned space within an object, such as a global, a malloc() result, or an automatic stack variable. An overlapping object with sub-objects, such as members of a structure, union, or array, which may have overlapping sub-objects. There are two kinds of objects: Heap / global, and ·stack.
[0112] Heap / Global Objects: Annotate heap / global objects with object metadata (see elsewhere for further meanings of other fields in this metadata besides spatial bounds enforcement). · Absolute start, Length relative to object start. Annotate a sub-object with sub-object metadata: Start against Object Start ·Length relative to the sub-object start.
[0113] Annotate heap / global pointers with (a) obj-id (object-id) and (b) subobj-id (sub-object-id) or, if the sub-object metadata is fairly small, an immediate encoding of the sub-object metadata. When accessing an object and its sub-objects, (a) For an object, use the obj-id to look up the object metadata (likely cached) in the object metadata table; (b) For sub-objects, if the sub-object metadata is encoded as an immediate in Abs-Ptr, find it there; otherwise, use subobj-id to look up the sub-object metadata (likely cached) in the sub-object metadata table; notice that some sub-object IDs can be annotated as page-related, allowing multi-page objects to reuse such sub-object IDs for such small sub-objects across different pages; and notice that the use of page-overflow-flag can prevent artifacts at page boundaries. The only way to access heap / global memory requires any access through absolute heap / global pointers (see references below), and this heap / global memory is: Heap / global memory that is bounded by the object metadata annotated to the pointer, i.e., the target (target-address / target-data-address) of a memory access (load / store) to the target is: start<=target<(start+length), where start is the object metadata start and length is the object metadata length, and Heap / global memory that is constrained within the bounds of the sub-object metadata annotated to the pointer, i.e., the target of the load / store is :start<=target<(start+length), where start is the sub-object metadata start = (object start+offset-from-object-start) and length is the sub-object length.
[0114] Heap / global pointers, on the other hand, are missing just enough bits to be able to work around this without having to go through the page class machinery: Annotate each data page with a page-class-id, and each pointer with a page-overflow-flag indicating that it belongs to the previous page so that an object can leak to another page if it finds it useful, thereby avoiding having objects of different page classes bounded by their multiple boundaries. Make object and sub-object IDs page-class-local (i.e., these IDs can be reused across different page-classes). A natural way to organize memory is to group contiguous pages that all share the same object and sub-object metadata into a range of pages annotated with the original page-class-id; for example, it is natural to do this for objects that are multiple pages in size, or for a slab allocator of objects that all share the same sub-object structure (and therefore the same sub-object metadata). While this embodiment requires hitting the object and sub-object metadata cache after getting the return value from the Page Table Entry (PTE), another embodiment may hit the PTE and then hit the object / sub-object cache if the information is duplicated in the PTEs for the pages corresponding to the object and sub-objects that span multiple pages, eliminating this serialization and potentially increasing complexity (e.g., when updating values in the cache) but also potentially increasing performance. Optimization: Since the in-memory sub-object metadata is all object-related, a slab allocator with uniform objects knows that the sub-object table can be reused across all objects.
[0115] The object metadata mechanism is completely independent of the sub-object metadata mechanism, so the entire sub-object metadata subsystem can be completely disabled either at run-time or when building the chip itself.
[0116] Stack Objects: In one embodiment, the entire stack is constrained to be 8 megabytes (MB) in size; in this embodiment, due to the encoding of stack object pointers, their size must be less than 1 / 2 kilobyte (KB); other encodings that do not impose these restrictions are possible. Compiler changes or source-to-source transformations at compile time can automatically heap-ify larger stack objects, not only by allocating the object on the heap rather than the stack, but also by deleting it upon return from the function that created its stack frame, just as if it remained allocated on the stack. Limiting the size of the entire stack allows pointers to be efficiently encoded using coordinates relative to the stack boundary, i.e., as pointer-uprel-to-stack-in-bytes; other Hard Object registers (stack-base-ptr, stack-limit-ptr) define the boundaries of the stack, and stack pointers can be expressed relative to them, thereby saving bits in the encoding. By limiting the size of stack objects, the three relative pointers to stack objects, specifically the object start, object end, and current pointer within the object, can be efficiently coded together by representing some of them relative to each other: · The object start can be expressed relative to a pointer using start-dnrelto-pointer-in-bytes. · The object end can be represented relative to the object start using size-in-bytes (i.e., by recording only the size).
[0117] C stack objects, whose size is not known at rest, are also problematic as they make stack placement unpredictable when the Hard Object compiler changes or other parts of the source-to-source transformation, so in this embodiment, C stack objects are also heapified by the transformation.
[0118] Time: Throughout this section, enforcing time bounds, including enforcing callee-save register integrity (which can be thought of as a kind of stack time bound on registers), prevents various kinds of use-after-free. In addition, there are two kinds of objects: Heap / global, and ·stack.
[0119] Callee-save registers are also a kind of memory that is shared over time.
[0120] Register callee-save safety: Hard objects require that a function call-then-return protects the integrity and privacy of the callee's callee-save registers (and the return address register, as well as the callee-save-reg-state itself) by: By requiring any registers accessed by the callee to have their caller values saved (on the stack) before first use, By protecting the caller's values in such registers while they are saved on the stack (not allowing them to be garbled while saved), and By requiring that the caller's value of any such saved register be restored (from the stack) (a) after the callee's last use of the register, and (b) before the function returns.
[0121] In the following, the embodiments of the present invention will be concerned with all of the registers that a Hard Object requires to be saved-then-restored in this way as "callee-save registers", as well as additional registers that are not formally "callee-save registers" in the RISC-V nomenclature, i.e. the return address register, and The state of the callee-save-reg-state automation itself, are also included under this term.
[0122] RISC-V® has both caller-save integer and floating-point registers. However, caller-save floating-point registers are likely only useful when function calls are made in the inner loop of a floating-point oriented program ("scientific code"). In contrast, almost all code uses integer registers. Therefore, in one embodiment, the compiler is configured to treat all floating-point registers as caller-save only. Doing this reduces the number of callee-save registers to the point where the entire callee-save-reg-state can fit into 64 bits. Create a callee-save-reg-state (finite state automation) register with the following sub-registers: In one embodiment, a where-saved array sub-register allocates a 4-bit sub-register for each applicable user register that records (a) where it is saved relative to the Hard Object framepointer, and (b) a special marker value (unsaved-marker-value) that indicates that the user register is not saved (including before saving it or after restoring it); A protected-range-max sub-register that counts the size of the protected range on the stack that the callee-save register is saved to, finding that this count can be more efficiently maintained as a count of machine words rather than a count of bytes, the may-restore-flag sub-register, see below, and the for-this-func-flag sub-register, see below.
[0123] It also maintains a which-register inverse map register that maps (a) stack locations to (b) the IDs at which the registers are saved, and updates this which-register inverse map whenever the callee-save finite-state automation is updated. Because the which-register inverse map is the inverse map of where-saved, this map contains only information that is also contained in the where-saved sub-register, and therefore the value of the which-register inverse map can be reconstructed from the value of the where-saved sub-register, and therefore does not need to be saved / restored when the callee-save-reg-state automation is saved / restored, i.e., When callee-save-reg-state is saved, the value of the which-register inverse mapping is discarded, and When the callee-save-reg-state is restored, it recreates the value of the which-register inverse mapping by inverting the where-saved mapping.
[0124] Another implementation of the which-register mapping is to simply create a content-addressable array in hardware that can look up the index of the array containing that value from a given value. This should be possible in hardware, given small arrays. This allows the which-register mapping to not appear separately in hardware, nor to be saved / restored to / from the stack. Using the where-saved array sub-register and the which-register inverse mapping, we maintain a bijection between (a) the callee-save registers and (b) the stack block address just below the framepointer, and each callee-save register must either (1) be left untouched or (2) be saved before use and restored before returning. That is, each stack checks the above mapping for memory accesses: · Do not allow access to (a) an unsaved callee-save register or (b) the stack location to which it is saved, except to save or restore that register. · Don't allow multiple callee-save registers to be saved to the same location. ·Do not allow saved registers to be garbled when the stack is accessed for other purposes.
[0125] If a callee-save register were written without being saved, information would be lost and there would be no way to return correctly to the caller. It turns out that Hard Objects use this mechanism to make it possible, since there are some no-return functions (such as exit()) that don't fault but simply clear the may-restore-flag register, and if that flag is cleared on return, the function is prohibited from even returning on fault, and the compiler can optimize away this. Using the callee-save-reg-state mechanism, the state of the callee-save finite-state automation itself can also be saved (using the special Hard Object operator save-callee-save-reg-state), preserved, and restored (using the special Hard Object operator save-callee-restore-reg-state) (including giving it a register ID) by treating it like a callee-save user register. The hardware needs to know whether the current value of the callee-save-reg-state register reflects the current function, or the state from the calling function (e.g., before saving it or after restoring it). The Hard Object tracks this using the for-this-func-flag register, so that when this flag is cleared, the Hard Object will not allow any operations that may require the callee-save-reg-state to be initialized. In the function call, check whether for-this-func-flag is true, and then reset for-this-func-flag to false. When callee-save-reg-state is saved to the stack (just like a callee-save register), it reinitializes the state for the new callee and sets for-this-func-flag. ·After callee-save-reg-state is restored, it turns out that for-this-func-flag is also canceled, but confirm this. · In the function return, check whether for-this-func-flag is false, and therefore set for-this-func-flag to true. When an operation that accesses a target register proceeds, if the target register is annotated as a callee-save register, it checks whether this for-this-func-flag is true, and if not, raises a fault.
[0126] The compiler, or any modification in the assembly it makes, needs to ensure that 4 bits are enough to record those locations some distance from the framepointer, and one way to do this is for the compiler to emit code that saves all of the callee-save registers in a contiguous block directly below the framepointer.
[0127] Heap / Global Time Notes: Annotate heap / global pointers and function pointers with time. Annotate heap / global objects and functions with version, version-ceiling (or refable-version-ceiling), and version-next-ceiling (or refable-version-next-ceiling). time: · Do all time / version arithmetic modulo the number of time bits (determined by the page class). Maintain the invariant that there are no significant formal pointers to objects with a time greater than the version and less than the version-ceiling. Calculations: When accessing an object through a pointer, it is required that the pointer time equals the object version. When calling a function through a pointer, it is required that the pointer time equals the function version. When an object is allocated, it only needs to return a pointer to the object from the deallocation list; the allocator cannot create a pointer to an object that has a new version, so the object cannot be accessed by non-system code until the allocator returns its pointer. When freeing an object, (1) increment its version, making it instantly inaccessible, and (2) furthermore, if its new version + 1 is equal to the version ceiling, it does not put the object back on the allocator free list, because otherwise it may not be freed, even if it is allocated again.
[0128] In one embodiment, the system allocator (sysalloc) owns unallocated objects (in the sense of modularity). When sysalloc allocates an object (e.g., using malloc()), it transfers ownership to a new owner (e.g., by taking the new owner from the caller-mod-owner register). When a client deallocates an object (e.g., using free()), the client first transfers ownership to sysalloc. Because sysalloc has dangerous powers, Hard Object allows sysalloc to increment object versions, and in this embodiment, non-hazardous user code may not be allowed to increment object versions. Periodic or on-demand version collection allows the memory allocation library to reuse objects whose versions have been exhausted. A ref-scan-phase is performed to undo the ref-flag for each stale pointer (a pointer whose pointer-time address is not equal to the refable-version of the object / function it points to) in register, stack, global and heap memory. (Undoing the ref-flag means that the pointer is no longer formal and therefore no longer usable (except for objects annotated to allow access via informal pointers, which is not a fault, see references below). ·Perform the obj-scan-phase, which "rotates" the version clock for each heap / global object, as follows: (1) object.version-ceiling:=object.version-next-ceiling, and (2) object.version-next-ceiling:=object.version. During allocation, when looking for unused object slots to reallocate, the memory allocator will not use object slots whose version cannot be incremented without doing so (on a subsequent free()) so as not to exceed the object.version-ceiling. That is, during free(), it can ensure that an object is no longer accessible by incrementing its version without first waiting for a version collection pass.
[0129] Another way to do this, which may be more efficient, is to do object.version-next-ceiling=objec.version just before the ref-scan-phase rather than just after it; this requires an additional object scan, but the version-next-ceiling will be more recent when used. Version collection can be done concurrently with an ongoing program, as long as care is taken not to cause the ongoing program to copy formal pointers from un-ref-scanned pages to ref-scanned-pages. Annotate (a) each data page PTE and (b) each register with a version-scan-state-enum that has one of three values: to-be-collected, collecting, or collected. (1) Before starting a version collection, annotate each page as to-be-collected. (2) When a page's ref-scan-phase begins, annotate the page as collecting. (3) When a page's ref-scan-phase ends, annotate the page as collected. When a formal pointer is read from a data page into a register, the annotations for that page are copied. When saving a formal pointer from a register to a data page, if the data page is annotated as collecting or collected and the register is annotated as to-be-collected or collecting (it is OK for the unscanned reference to be copied to scanned memory), the formal pointer will be scanned again ("just-in-time") before saving it. Optimization: Multiple ref-scan-phase-s can be done before doing an obj-scan-phase, and all but the last ref-scan-phase-s can be "best effort", i.e., the above mechanism can be ignored, possibly allowing some old-style pointers to survive but significantly reducing them as long as they do not trigger the above just-in-time scan, and reducing the occurrence of just-in-time scans during the final non-best-effort ref-scan-phase.
[0130] Version collection can be done with memory hierarchy in mind: (1) The ref-scan-phase scan of a formal pointer has two parts. · from-part: Find the pointer and ask if it is formal, · to-part: Find the object pointed to by the pointer and ask if the pointer is older than the object. (2) Memory ordering, the preferred way to scan through memory (allowing pre-fetching), and depending on the architecture, from-part can be done respecting the memory hierarchy by scanning pages, only affecting the cache of the core doing this scan. (3) to-part can be done with memory hierarchy in mind as follows: 1. Annotate each data page with obj-scan-active-flag, but do this using a bitmap instead of a bit in a PTE so that software doesn't have to swap to that page to access that bit. 2. Before starting the ref-scan-phase, set obj-scan-active-flag for each memory-resident page. 3. During the from-part of a formal pointer scan, if the pointed-to object is in a page that either (a) has a clear obj-scan-active-flag or (b) is not resident in memory (it has been paged out since the start of the scan), do not scan, and in this second case, cancel the obj-scan-active-flag for this page. 4. During obj-scan-phase, only scan pages that still have obj-scan-active-flag set and are resident in memory. The constraint that obj-scan-active-flag updates the version number and only scans pages with obj-scan-active-flag set is a requirement for correctness; pages with obj-scan-active-flag set at the end of ref-scan-phase are pages that contain objects whose to-part of ref-scan-phase was visited throughout the entire ref-scan-phase; i.e., these are the only objects that are sure in obj-scan-phase to be visited during ref-scan-phase if there is an old visible formal pointer to the object, and have its ref-flag revoked (because it is old), so that there are no visible obsolete formal pointers to this object. (4) Here, the only memory traffic other than that collected by version collection is the in-order scan. (5) Also, objects that are not in pages with a clear obj-scan-active-flag are unlikely to need scanning, since they may not be "hot" anyway. Excluding a page where all of its objects have exhausted their available versions, and therefore it is natural that the page will never be used again (the page is completely cut off, so there will be no new uses for any of its objects), the version collection process may intentionally swap such a page into memory so that it can be scanned, thereby updating its object versions and making the objects available again. Version collection is also incredibly parallel in both the ref-chan-phase and the obj-scan-phase, making it easy for multiple threads to partition the work and do it in parallel. This is not the case with garbage collection. Hard objects therefore do not require garbage collection because they eliminate the problem of using freed memory unrelated to garbage collection. The Hard Object Version Collection algorithm is fundamentally better than garbage collection in some algorithmic terms, and has the following properties that garbage collection does not have: Incredible parallelism Simultaneous with the execution of user processes Be mindful of memory hierarchy, and If done frequently, never leaves dead memory, On the other hand, junk collection requires a significant amount of memory to die before it is of any use.
[0131] Stack Time: Hard Objects exploit the fact that stack addresses represent a total order, where newer is more. We also assume (as many, if not all, prior art systems do) that the stack grows downwards. This assumption therefore connects stack frame positions in space with stack frame relationships in time. Hence, for stack addresses S1 and S2, we define S1 to be newer than S2 if S1 is less than or equal to S2, and recall that total order is a binary relation such that Antisymmetric (for all a, b: a≦b and b≦a, a=b) transitive (for all a, b, c: a≦b and b≦c necessarily imply a≦c), and · Undirected connection (for all a, b: a≦b or b≦a). Formal pointer required for stack access Don't allow functions to write to heap / global memory unless they have dangerous powers. · Two types of formal stack pointers are created: (a) stack-pointer and (b) stack-object-pointer. Frame Pointer: A Hard Object maintains a non-garbled Hard Object framepointer that is independent of the program's user framepointer. This Hard Object framepointer is encoded into the return-and-frame pointer woken by the call (jal / jalr) instruction. It is restored from encoding by the return (jalr) instruction. Stack Pointer: It is ephemeral: therefore, it cannot return to the caller. Stack Object Pointer: Use the framepointer of the frame containing the stack object pointed to by the stack object as a kind of stack time in a stack object pointer. (In embodiments, this framepointer annotated to a stack object pointer may be called a stack-object-pointer-frame-pointer; if the framepointer is annotated to the target of a memory access, in embodiments, it may be called a target-frame-pointer; if the framepointer is a frame pointer annotated to the value being loaded / stored, in embodiments, it may be called a value-frame-pointer.) Annotate this stack time into the formal stack object pointer. Hard Objects do this effectively using framepointer-uprelto-stack-in-qwords, since frames are typically quad-word aligned and the other Hard-object registers (stack-base-ptr, stack-limit-ptr) contain stack delimiters, so the framepointer can be expressed relative to them. Stack time is a softer kind of ephemeral, and Hard object means that a stack object pointer cannot escape for the duration of the frame containing the stack object it points to, and this property is easily enforced by the fact that saving a stack pointer to this stack (to stack object S1) is done through one of two kinds of pointers: (a) saving through a stack pointer, in which case it is only allowed for the current frame, or (b) saving through a second stack object pointer (to stack object S2) that also has stack time, in which case it is not allowed to save a stack object pointer (relative to stack object S1) to the (second) stack object (S2) if that second stack object (S2) will outlive the object (S1) pointed to by the stack object pointer being saved. The above dynamic escape analysis of stack object pointers ensures that when using such frame pointers as a form of time, there is no ambiguity, since no stack object pointer will outlast the frame of the object it points to. This process works well for standard C code in the case of a single stack frame when the calling function passes a pointer to one of its stack objects to the called function, allowing the called function to read / write the body of that object to its caller without failure. Specifically, this works because (a) when the software tool chain knows that the address of a stack object is being taken, it automatically transforms the calling code to insert a call into a special Hard Object narrow pointer arithmetic, (b) at runtime, the caller transforms this Hard Object narrow pointer arithmetic from stack-pointer to a concrete stack-object-pointer (which also has an argument giving the size of the object, inserted by the compiler from static information in the program where it thinks this type of size is allocated to C), and (c) at runtime, the callee, when it accesses the caller stack object, it does so through the stack-object-pointer (note: not stack-pointer), which is not constrained by the Hard Object framepointer (as opposed to being seen through stack-pointer).
[0132] Heapized Stack Objects: For various reasons, in this embodiment, some stack objects need to be "heapized" (allocated on the heap), even though they still act as stack objects in that they are deleted when the function that allocated them returns (such objects are semantically stacks, but implicitly heaps). If, as part of dynamic escape analysis, these heapized stack objects were allocated as regular heap objects and stack-obj-pointers were prevented from escaping their frame's lifetime, Hard Object would prevent stack-obj-pointers from being stored in such heapized stack objects. This restriction is problematic because it prevents some corrective programs from proceeding without failure. To overcome this, one embodiment of Hard Object annotates the framepointer / stack time to the Abs-Ptr of heapized stack objects, causing Hard Object to treat this pointer as a stack-obj-pointer. When a stack-object-pointer is stored in a heap object, Hard Object checks if this framepointer / stack time annotation for the heap object is stored, and if it is stored, it uses the annotated stack time as if it were stored in a stack object and treats the heap object as a stack object, i.e. it does not allow the object to be stored longer than the stack-obj-pointer is stored. Similarly, if an abs pointer to a heapized stack object is written somewhere, this pointer is subject to the same escape analysis constraints as a stack-obj-pointer.
[0133] One implementation of this annotation of stack time for heapized stack object abs pointers (heap pointers) is to put the stack time in the heap object metadata and read it only when the stack time of the heap object is needed. Another implementation is to simply maintain a mapping from abs pointers to stack times, for example using a red-black tree or a skip list. However, when implemented, this mapping from abs pointers (heap pointers) to stack times may be cached in a heap-to-stack-time cache. (When deallocating or reallocating heap objects, the system allocator can update this cache entry to prevent cache poisoning, even if the user function never deletes the heapized stack object.)
[0134] Prevent data leakage across memory reuse Heap: The memory allocator library wants to prevent leakage across free-then-alloc object reuse. Annotating the machine code with clearing metadata tags is significantly more efficient than actually writing all of the data, since in one embodiment, 16 machine codes are annotated with one tag metadata code (i.e., one 64-bit machine code has a 4-bit metadata tag). · Cancel the ref-flag-s attached to the machine code of the object data, and by doing this, the leakage of formal pointers is prevented. · By canceling the written-flag-s attached to the machine code of the object data, data leakage is prevented. Stack:Create a stack-floor register: · Do not allow access to stack data below stack-floor. Decrement the stack if and only if it is written below stack-floor. Receive the return and increment it to the new stack pointer.
[0135] Therefore, the stack-floor register forces a reinitialization of the frame before reading it, a natural progression that a program would normally follow anyway. However, when using an unqualified compiler, stack frame writes may not be initialized as expected by the compiler, such as when the compiler skips stack words, thereby leaving gaps in the stack memory being written, thus necessitating statistical analysis to ensure stack frame writes are in actual stack order and do not leave gaps in the stack memory being written. Because this statistical analysis is for assembly or machine language, and because variable-sized objects may be removed from the stack using heapization, it seems straightforward to justify this statistical analysis without unduly conservative approximations.
[0136] This can be useful to allow for adapting the "frayed edge" to user notification of the stack floor by allowing some out-of-stack-frame-order writes. Create a sub-stack-floor-init-flags register that acts as an init-flags array whose array coordinates are relative to the stack-floor: This register is used to flag some machine words that, when written to, will come just below the stack floor, and when so flagged, will treat that machine word as if it were on or above the stack floor, even though it is below the stack floor. When the stack-floor moves, it updates the original coordinates of the sub-stack-floor-init-flags, and of course, in doing so, it shifts all of the flags, i.e., the sub-stack-floor-init-flags then undergo a shift operation which shifts each index so that each init-flag has the value of its previous index + 1, and then shifts the index + to a false init-flag value that is not in the range of the array, and if the first init-flag in the array is true, it sets the sub-stack-floor-init-flag, then repeatedly decrements stack-floor (assuming the stack is growing) and performs a sub-stack-floor-init-flags shift operation until the first init-flag in the array is false. Keep the flag array and stack-floor in a canonical state where stack-floor is as low as possible (the stack is growing), and if the first flag in the array is set, stack-floor may only be lowered so that, implicitly, the first flag is never set, but if a stack data word corresponding to that flag has ever been written that would have set this first flag, then, in the move, stack-floor is lowered and the flag array is shifted so as to "evict" all of the set flags, again restoring the invariant that the first flag is never implicitly set. If your program always writes monotonically down the stack, then sub-stack-floor-init-flags is optional (assuming the stack grows).
[0137] Software engineers want to prevent callee stack frames from aliasing objects in the caller stack, so they create a stack-obj-floor (stack-object-floor) register that is kept at or below the bottom of the lowest (assuming the stack grows) stack object whose address is taken. Since creating a stack-obj-pointer requires special Hard Object narrow pointer arithmetic, a Hard Object can maintain this invariant in its order by lowering stack-obj-floor to the bottom of the newly narrowed stack object. · Upon receiving return, the Hard Object becomes stack-floor := Hard Object framepointer, which makes stack-obj-floor := caller stack-floor (i.e. the caller function that return returns from), which is the same value here.
[0138] At call time, the Hard Object asserts that the stack pointer must be less than or equal to stack-obj-floor, thereby ensuring that the callee is not aliased into any stack-object-pointer range that its stack frame already spans at the time of the call to the callee.
[0139] Modularity A module identification is denoted as a module ID, and the space of module IDs is viewed as a binary tree: The module owner is an internal node in this binary tree (module-id / mod-owner-id and module-owner-suffix-length / mod-owner-suff-len). Module ownership is the leaf of the binary tree (module-id), If no module-owner internal node can be an ancestor of another, then the module-ownable sets of various modules are disjoint. Notes: If you are a module owner, enter the code (text), Data in case of module ownership possibility Register: current-mod-owner: the module owner annotated to the function at the time, maintained by the hardware caller-mod-owner: The module owner of the immediate caller; it is guaranteed to be corrected by the hardware on the first instruction of the function, but this register is caller-save, so if it is not saved by the client code it will be lost on the next call.
[0140] Annotate each of the following with public-flag. Heap / Global Machine Language (Data Dword-Meta-Datum) Heap / Global Objects
[0141] Annotate each of the following with public-target-flag: Absolute pointers (durable and ephemeral)
[0142] Memory accesses (reads / stores) are exposed if: All public-flag-s (both public-flag in Object-Metadata and public-flag in Dword-Metadata) that apply to the data being accessed are set, and Any public-target-flag is set on the pointer accessed through it.
[0143] Text / code (annotated with module-owner) cannot access data (annotated with module-ownable) unless: publicly accessible, or The module-id of the text matches the module-id of the data's mod-ownable, except for the last module-owner-suffix-length bit of the code's mod-owner (i.e., the data's module-ownable is a leaf in the subtree of the text's module-owner internal node).
[0144] In one embodiment, the above field, the module-owner field, uses the following bits: mod-owner: 19 bits consisting of: mod-owner-id: 15 bits, mod-owner-suff-len: 4 bits
[0145] Ownership Transfer and Integrity: An object owner can transfer ownership of an object to another module by annotating each object with an integrity-flag. Only the owner of an object can set the integrity flag for that object. When ownership of an object is transferred, the Hard Object revokes the integrity-flag of that object.
[0146] Standard load / store instructions cannot access objects that have an explicit integrity-flag. Special non-integrity load / store instructions / operations can access objects that have an explicit integrity-flag, but cannot access objects that have a set integrity-flag. These special non-integrity memory-access operations may not be implemented as hardware instructions. Besides preventing Trojan horse attacks [homer-8th-cent-bc] through the use of ownership transfer, integrity-flags may also be useful to prevent access to an object (e.g., by another thread) while it is being initialized or whenever its invariant is violated.
[0147] References Follow ref-flag everywhere in all data machine code. Notes: Each machine code in the heap / global / stack data is annotated with a reg-flag using the corresponding flag in the metadata memory. (a) Each user integer register is annotated with a corresponding flag in the special int-register-ref-flags, and (b) each control status register that can hold a user pointer is annotated with reg-flag using a special flags register. Propagation: Only sysalloc can set the ref flag for non-reference machine code (except in some configurations where the module itself is configured to allow this), The reg-flag is copied in parallel with the data being copied by the move instruction. The reg-flag is cancelled for any other operation that sets a machine value. The ref-flag set in the machine code means that it is a formal pointer or reference (ref). Formal pointers contain various metadata fields along with their pointer values, which may be specially encoded. All formal pointers / references contain or are annotated with a Ptr-Kind-Enum and possibly an Abs-Ptr-Kind-Encoding that tell what kind of pointer they are.
[0148] Hard Object Pointers have two aspects: Formality: formal vs. informal: pointers with or without ref-flags, Structuredness: Structured vs. Unstructured: Depends on what the Ptr-Kind-Enum and Abs-Ptr-Kind-Encoding bits embedded in (or annotated to) the pointer indicate about how the pointer is encoded.
[0149] Types of structured pointers: Absolute heap / global (heap-global memory): (a) durable or (b) ephemeral (including stack pointers), stack objects, Function pointers / forward text pointers, return pointer, Unstructured Low and High Pointers: Hard objects correspond to raw pointers with no annotated metadata other than a Ptr-Kind-Enum that identifies them as raw pointers, and it turns out to be convenient to have the encoding of this Pri-Kind-Enum be all zeros or all ones, so that sign-extended raw integers are raw pointers by default when the address bits are sign-extended to fill the word (e.g., prior art x86-64 designs require that the high-order bit of a pointer be a sign-extended copy of the highest-order bit used for actual addressing).
[0150] It is known that unstructured low and high pointers are also raw pointers. The encoding of a structured pointer cannot be faked by user code if the pointer is also a formal pointer.
[0151] Return Pointer / Ret-and-frame Pointer The Hard Object encodes the framepointer in ret-and-frame, and the Hard Object encodes this ret-and-frame starting from the stack pointer at the point of the call (when ret-and-frame is created by a call / jal / jalr instruction). The Hard Object reduces the number of bits required to encode the framepointer by encoding it as framepointer-uprelto-stack-in-qwords as follows: framepointer is expressed in quad-word units (the compiler checks whether frames are quad-word aligned or are likely to be configured to be quad-word aligned), · Express framepointer as the other hard-object stack delimiter registers (stack-base-ptr, stack-limit-ptr) rather than as an absolute pointer.
[0152] Consider a return to a target address through the return pointer / ret-and-frame. After the return, the Hard Object needs to know the function start of the function that control has just been returned to so that it can set current-function-start to point to it. This is done as follows: Annotate the return pointer with the function-top-offset: the page-local part of the current-function-start of the target address (typically the low 12 bits). If function-top-offset is non-zero, then current-function-start is the (1) target address with the (2) page-local part (usually the low 12 bits) replaced by the function-top-offset of the return pointer. Otherwise, if function-top-offset is zero, search the Text-Page-Metadata of the target address to find its func-at-page-start field and use this as the current-function-start.
[0153] At call time, when constructing the return pointer (in RISC-V it is stored in the ra register), the above scheme works for return: from finding the func-at-page-start, the return pointer looks up the Text-Page-Metadata of the instruction to return to (usually the address of the next instruction). If func-at-page-start is the current value of the current-function-start register, create a return pointer with a function-top-offset of zero. Otherwise, if func-at-page-start is not the current value of the current-function-start register, then the current function starts on the current page, so set function-top-offset in the page-local part (usually the low 12 bits) of current-function-start.
[0154] To constrain control flow, Hard Objects need to ensure that the return pointer does not escape, so they enforce the invariants that the return pointer (1) must not be returned from a function, and (2) must not be stored in heap / global memory by filtering out code with dangerous exponents (so that features like setjmp / longjmp can be used).
[0155] Ephemeral: Ephemeral absolute pointers cannot be stored in heap / global memory, even by dangerous system code (they can be excluded using special modes or special store instructions, with standard store instructions in standard execution mode), and they must not be returned from functions. Decaying ephemeral pointers: Durable pointers can be copied to produce a pointer with the same properties as any other code. Amplifying durable pointers: Ephemeral pointers can be copied to produce a pointer with the same properties as only the owner of the object they point to. Ephemeralness solves one of the major problems with capabilities: if a client gives a library a capability, when the library returns, the client has no way of knowing whether the library has saved a copy of that capability for later use, unless the capability is ephemeral. Libraries expect to pass around absolute ephemeral pointers passed in by clients, sometimes returning them internally, but the Herd Object does not allow this return. One workaround is to have the top library function save a client ephemeral pointer on the stack and then rotate that stack pointer internally.
[0156] Mutability Making data immutable greatly improves your ability to reason about the semantics of your program: once an immutable object has meaning (it's initialized / constructed), that meaning never changes.
[0157] Functional programming is a style of programming in which objects are allocated but not modified, i.e., never written to after they are first initialized. Making programs even partially functional can greatly increase the programmer's ability to reason about their correctness. To this end, hard objects provide the ability to make data read-only / immutable (known as "const" in the C programming language).
[0158] Annotate each with writable-flag Heap / Global Machine Words (Data-Dword-Metadata), and · Heap / Global · Objects (Object-Metadata).
[0159] Annotate each with writable-target-flag Absolute pointers (persistent and transient), and Stack object pointer.
[0160] Access is writable if All writable-flags-s- (both Object-Metadata writable-flags and Dword-Metadata writable-flags) associated with the data being accessed are set, The writable-target-flag of the pointer being accessed is set.
[0161] It needs to be writable and writeable.
[0162] A mechanism for annotating hard object metadata into data or code Hard objects provide the above semantics as follows: Annotate data and code with metadata, This metadata is then checked when data or code is accessed or executed.
[0163] This subsection lists various mechanisms for annotating metadata on data. All of these mechanisms are cacheable in the sense that a hard object can find a key that can be used to cache each one. This was proven by actually implementing each cache in a hard object software simulator. Furthermore, the simulator obtained a high cache hit rate, and the percentage of total cache memory traffic that was hard object metadata (to memory, on the other side of the cache) was a low percentage of total memory traffic. Caching strategies for each type of metadata are described in more detail below.
[0164] Additional Registers: Hard objects use some additional dedicated registers. One way to do this is to use the RISC-V® Control and Status Register Extension mechanism. Hard objects add registers to delimit various boundaries, such as the stack, metadata tables, and ranges of various types of specialized memory. Hard objects delimit the stack using stack-base-ptr (the semantic bottom of the stack, which is the top of the stack memory as the stack grows) and stack-limit-ptr (the semantic top of the stack, which is the bottom of the stack memory, so as the stack grows it will be smaller than stack-base-ptr). These registers only need to be saved / restored on a context switch. If metadata tables are embedded in virtual memory, the HO delimits them so that they are protected from user programs. For each such type of metadata, the HO delimits them using a pair of pointers: metadata-array-hi-ptr and metadata-array-lo-ptr. These registers only need to be saved / restored on a context switch.
[0165] The hard object maintains transient state related to the current module (current-mod-owner). This is updated on call and returned from the current function Function-Metadata. It does not need to be saved / restored on the stack. The hard object maintains transient state related to the start of a control flow transfer, such as the caller module (caller-mod-owner), information about the start of the control flow transfer (control-flow-kind), and a callee-ret-and-frame-ptr which is the Ret-And-Frame-Ptr of the function that just returned and helps calculate current-function-start after the return. This is updated on every instruction, so it does not need to be saved / restored on the stack.
[0166] Hard objects use simple bit flag array registers to annotate (a) integer user registers, (b) floating-point user registers, and (c) control status registers. The following metadata is therefore not provided except where some combinations do not make sense, such as reference flags for user floating-point registers. Because these registers annotate user registers, they are updated in-place and do not need to be saved / restored to the stack: ref-flags register (int-register-ref-flags, csr-ref-flags), · written-flags register (int-register-written-flag-s, float-register-written-flag-s, csr-written-flag-s).
[0167] Hard objects maintain registers that user programs use to communicate which registers can pass through a call or return. These registers are not saved / restored across calls / returns, and software tool chains are expected not to insert calls / returns between these sets of registers and uses of calls / returns intended to annotate: the register-ok-to-return-flags register, and The num-int-args-in-registers and num-float-args-in-registers registers (note that in an alternative embodiment, these flags are replaced by the register-ok-to-call-flags register).
[0168] Hard objects maintain a parallel hard-object frame pointer (or shadow-frame-pointer) to the user frame pointer, which is not writable by user code. It is saved (at least in RISC-V) in the Ret-And-Frame-Ptr created by the call / jal / jalr instructions. It is restored on return from (a) stack-limit-ptr and (b) the framepointer-uprelto-stack-in-qwords field of the Ret-And-Frame-Ptr.
[0169] Hard objects maintain stack-floor and stack-obj-floor registers. Hard objects also maintain sub-stack-floor-init-flags to annotate initialized stack machine words even when they are below the stack floor. These stack floor mechanisms are updated where they do not need to be saved / restored to the stack. Stack floor is updated by user writes and placed at the callee frame pointer on return. stack-obj-floor is placed when a hard object narrow pointer call is made to create a new stack object and restored to the stack pointer on return. sub-stack-floor-init-flags is updated by user writes and cleared on return.
[0170] A hard object annotates the callee-save-reg-state mechanism in registers as a finite state machine describing the current frame. The state of this automaton must be saved / restored to / from the stack on every call / return, but the callee-save mechanism itself ensures the integrity of this stack state in the same way that it ensures the integrity of the user's callee-save registers (and return address register).
[0171] Hard objects provide registers that can turn parts of the hard object off when bootstrapping the hard object in a new process during metadata setup or when switching to kernel mode: hard-object-active-flag, callee-save-active-flag; these registers are updated after program initialization by the C run-time (CRT0) or when in kernel mode to indicate whether parts of the hard object are active or not, turning them off or on on context switches to or from the kernel.
[0172] Embedding Metadata into Structured Pointers: Multiple types of structured pointer encodings are possible, as long as a Ptr-Kind-Enum field is shared by all of these structured pointer encodings that can be used to distinguish between different types of encodings. The different types of encodings use different techniques to annotate the data with metadata. Prior art 64-bit machines typically do not need all 64 bits to encode addressing, so these bits can be used for other purposes, such as encoding metadata. For example, if 39 bits of a 64-bit pointer are used to encode the address the pointer points to, the remaining 64 - 39 = 25 bits (the "hi bits") can be used to annotate the pointer with metadata. Decoding the metadata into a structured pointer may require further decoding stages that also reference other metadata, such as other hard object registers or metadata maps.
[0173] Whenever a formal pointer is located, the machine word is annotated with a reference flag to indicate that it is a formal pointer, so that the machine word cannot be confused with an integer. The reference flag annotated to the pointer tells the hardware that the machine word in question is a proper pointer, not a regular integer. If the official pointer is a structured pointer, the structure of the structured pointer can be trusted, since it cannot be directly modified by user code (see ALU interception below).
[0174] Whenever a program tries to "look up" a formal pointer, it does so using an ALU operation. Because the hard object intercepts all data flow into and out of the arithmetic logic unit (ALU), when the hard object machine detects a formal pointer reference flag, it can modify what the ALU is inputting and what it is outputting. Thus, the hard object's encoding is invisible to the user program. No formal pointer can be "seen" except as intended by the hard object. For formal pointers, the hard object can modify the ALU to prevent corruption of its metadata and to ensure the correct propagation of that metadata. For example, during pointer subtraction, you can remove the metadata before the subtraction to achieve the intended result. For example, when used in an arithmetic context where the operation expects an integer, the formal pointer is intercepted on its way into the ALU, the metadata is removed, and the pointer is converted to an integer representing the raw pointer address, which is what a program running on a prior art non-hard object system would expect.
[0175] When processing a formal pointer, depending precisely on the ALU operation, the metadata is removed by the hard object on its way into the ALU, the ALU operation is performed, and the metadata is re-annotated to the formal pointer on its way back out. The hard object then checks that if the pointer is intercepted again, as it does the next time the formal pointer returns to the ALU, the formal pointer decodes to the same integer value that was output just before the hard object turned the metadata back on, and if not, a fault occurs. (That is, if the ALU operation puts information in any of the high-order bits of the integer that the hard object uses for metadata, the ALU operation faults.) The result is that (at least in this respect) if the user's program does not trigger a hard-object fault, it operates the same as if it were running on a non-hard-object machine.
[0176] Hard objects intercept loads and stores, so when a load or store is made through a structured pointer, metadata can be used to influence the hard object's checks on whether the load or store is allowed. The Abs-Ptr-Kind-Encoding enumeration indicates the encoding of the pointer and includes: ·lo-unstructured-APKE, Durable-ABS-APKE, ·ephemeral-abs-APKE, ·hi-unstructured-APKE.
[0177] The Immediate-Granularity enumeration indicates the granularity of the sub-object immediate encoding of the pointer and includes: · Byte-IG, Half-IG, ·word-IG, ·dword-IG.
[0178] The Subobj-Id-Namespace enumeration indicates the subobject encoding of the pointer and includes: Top-SIDN, Bottom - SIDN.
[0179] In this detailed description, any pointer to data may be called a data pointer. An Abs-Ptr (either an absolute pointer, absolute heap / global pointer or stack pointer) contains: abs-ptr-kind-encoding (Abs-Ptr-Kind-Encoding), note that this annotation implicitly includes the encoding of the persistent flag. subobj-immediate-flag, public-target-flag, ·writable-target-flag, time (time or time-address of heap / global pointer), obj-id, In indirect mode (when subobj-immediate-flag is false), it also contains (1) subobj-id-namespace and (2) subobj-id (which can be called subobj-topid if subobj-id-namespace=top-SIDN). Immediate mode (if subobj-immediate-flag is true) includes: (1) Immediate-Granularity, (2) immediate-dist-to-start (the distance from the start of the object to the start of the subobject in units of immediate granularity), (3) immediate-length (the length of the subobject in units of immediate granularity), page-overflow-flag, · target-address / target-data-address: (1) data-page-index (the part of target-address that selects the page), (2) data-page-offset (the part of target-address that points within the page, typically 12 bits in prior art embodiments).
[0180] The immediate-subobject-start can be calculated as the sum of the object start and immediate-dist-to-start times multiplied by the immediate granularity. The immediate-subobject-end can be calculated as the sum of the immediate-subobject-start and immediate length multiplied by the immediate granularity. In one embodiment, if a machine word has 64 bits, of which 39 bits are used to encode the target address, then the above fields use the following bits: abs-ptr-kind-encoding: 2 bits. · subobj-immediate-flag: 1 bit. · public-target-flag: 1 bit. ·writable-target-flag: 1 bit. Time: 2 bits. obj-id: 9 bits. Indirect and immediate modes are unions (in the C sense: one or the other is used) depending on the subobj-immediate-flag (union tag). 8 bits; see below for details. ·page-overflow-flag: 1 bit. data-page-index: 27 bits, and data-page-offset: 12 bits.
[0181] Indirect and immediate modes are a union (either one is used) depending on subobj-immediate-flag (union tag). 8 bits: Indirect mode (8 bits): (1) subobj-id-namespace: 1 bit. (2) subobj-id: 7 bits; Immediate Mode (8 bits): (1) Immediate Granularity: 2 bits. (2) Immediate Distance to Start (distance from the start of the object to the start of the sub-object in units of immediate granularity): 3 bits. (3) Immediate Length (length of the sub-object in units of immediate granularity): 3 bits.
[0182] Using page-overflow-flag and page-class-id: When performing pointer arithmetic on Abs-Ptr, if the new value of the pointer ends up pointing to a new page with a different page-class-id than the original pointer value, and the new page is one page after the range of pages annotated with the original page-class-id, the hard object sets the page-overflow-flag to the new value. Doing this means that on subsequent uses of the pointer, the hard object first decrements the pointer's data-page-index by one page before looking for the data page PTE (to find the page-class-id). This means that the hard object uses the data page PTE of the previous page. Optimization: When caching PTE meta-datum, it caches the PTE meta-datum of the previous page in the same cache entry. This allows the same cache entry to be used to satisfy requests when the pointer has the page-overflow-flag set. · Pointer arithmetic that attempts to point to multiple pages after a range of pages annotated with the same page-class-id as the original pointer will fail. Pointer arithmetic that attempts to create a pointer before a range of pages annotated with the same page-class-id as the original pointer will fail.
[0183] Stack-Obj-Ptr (stack-object-pointer) contains: Ptr-Kind-Enum, ·writable-target-flag, framepointer-uprelto-stack-in-qwords, the framepointer-uprelto-stack in qwords (quadwords). Note that this field corresponds to a compressed encoding of the value of the hard object frame pointer (shadow-frame-pointer) when this stack-object-pointer points to the bottom frame (stack-object-pointer-frame-pointer). start-dnrelto-pointer-in-bytes, which is the start-dnrelto-pointer (or target-start-dnrelto-pointer / target-start-down-relative-to-pointer) in bytes. size-in-bytes, which is the size in bytes (or target size), and pointer-uprelto-stack-in-bytes, which is the pointer-uprelto-stack (or target-pointer-uprelto-stack / target-pointer-up-relative-to-stack) in bytes.
[0184] Define the stack pointer's stack pointer target as stack-limit-ptr plus stack pointer's pointer-uprelto-stack-in-bytes. Define the stack pointer's stack-pointer-start as stack pointer's stack-pointer-target minus stack pointer's start-dnrelto-pointer-in-bytes. In one embodiment where the stack is 8MB, the above fields use the following bits: Ptr-Kind-Enum: 3 bits, ·writable-target-flag: 1 bit, framepointer-uprelto-stack-in-qwords: 19 bits, start-dnrelto-pointer-in-bytes: 9 bits, size-in-bytes: 9 bits, and pointer-uprelto-stack-in-bytes: 23 bits.
[0185] Text-Ptr-Kind-Enum indicates the kind of text pointer, and includes: forward-Text-TPKE, and ·ret-and-frame-TPKE.
[0186] Forward-Text-Ptr (forward text pointer / function pointer / function function) contains: Ptr-Kind-Enum, ·text-Ptr-Kind-Enum(Text-Ptr-Kind-Enum), target-address, durable-flag, ·function-body-target-flag, cross-module-target-flag, and Function time (or time address or time-address).
[0187] In one embodiment where program text memory is limited to 4 GB, the above fields use the following bits: Ptr-Kind-Enum: 3 bits, · text-Ptr-Kind-Enum(Text-Ptr-Kind-Enum): 1 bit, · target-address: 32 bits, durable-flag: 1 bit, function-body-target-flag: 1 bit, cross-module-target-flag: 1 bit, and Function time (or time address): at least 1 bit.
[0188] The Ret-And-Frame-Ptr (ret-and-frame / return pointer) contains: Ptr-Kind-Enum, ·text-Ptr-Kind-Enum(Text-Ptr-Kind-Enum), target-address, Function-top-offset-in-dwords, which is the function-top-offset in dwords (double words). framepointer-uprelto-stack-in-qwords (or framepointer-uprelto-stack / framepointer-up-relative-to-stack in qwords (quad-words)), and Function time (or time address).
[0189] In one embodiment where program text memory is limited to 4 GB and text target addresses are half-word (16-bit) aligned, the above fields use the following bits: Ptr-Kind-Enum: 3 bits, · text-Ptr-Kind-Enum(Text-Ptr-Kind-Enum): 1 bit, target-address: 31 bits (semantically a 32-bit value with the low-order bits assumed to be 0), function-top-offset-in-dwords: 9 bits, framepointer-uprelto-stack-in-qwords: 19 bits, and Function time (or time address): 1 bit.
[0190] Passing a Stack-Obj-Ptr or Ret-And-Frame-Ptr from the stack context: If Stack-Obj-Ptr or Ret-And-Frame-Ptr is passed from its stack context, such as a kernel routine or another thread, the pointer becomes meaningless, since stack-limit-ptr has fields defined only relative to it. Because this context passes through the dangerous code, techniques for dealing with this situation that are not available to normal user code may be used. In one embodiment, if such a pointer is passed out of context, its reference flag is cleared. Now, the pointer is still structured, but it is no longer formal, so it cannot be accidentally used out of context. Additionally, embed the structured pointer into a larger structure that contains meaningful contextual identifiers, such as thread-id or stack-limit-ptr. · We can allow dangerous code that wants to use such pointers by providing a dedicated hard object operator and passing stack-limit-ptr as a second argument to the operator used during pointer decoding. A dedicated hard object operator can be provided to allow dangerous code that wants to be created formally again, and that is trusted to check that it is passing a pointer to a context with an appropriate stack-limit-ptr before creating it formally. For example, code that restores a trap frame might be a good place to do this.
[0191] Page Table Entry: Page granularity metadata is annotated to text and data pages by simply adding fields to the Page Table Entry (PTE) or by creating a parallel Page Table Entry Map that functions similarly to a standard traditional virtual memory page table. This is a simple and proven mechanism for annotation. The text-page meta-datum (Text-Page-Metadata) contains: · func-at-page-start: A pointer to the start of the function that contains the first instruction on the page.
[0192] Of course, every text-page meta-datum is associated with a text page, which has a page-start-address, the start address of that page. The hard-object design uses this page-start-address to handle restoring the current-function-start on return. Of course, the map from text addresses to the text-page metadata for that page can be cached in the standard way, but in addition, since most references to this map require the metadata of the current text page, it is also very efficient to cache the map entry for the current text page in dedicated current-text-page-base and current-text-metadata registers for that purpose. A data-page meta-datum (Data-Page-Metadata) contains: · page-class-id: Identifier for the Page class meta-datum, i.e., an index into the Page class metadata table. ·page-subobj-id-abs-base.
[0193] Caching page table entries as a function of page index (target address without on-page bits, typically the lower 12 bits) is a solved problem, and therefore the prior art works.
[0194] Tag: Per Machine Word Flag: Tag metadata is annotated with (or associated with) each machine word (a 64-bit "double word" in one embodiment) using a simple memory map such as a pairwise array with main memory. For example, if a machine word is 64 bits and the tag per word is 4 bits, this results in a size reduction of 64 / 4=16. Hard objects require 4 tag bits of metadata per machine word. · Text-Dword-Metadata contains a single flag annotating the machine word. Data-Dword-Metadata contains a collection of four flags annotated to the machine word.
[0195] Text-Dword-Metadata / textDword-Meta-Datum / textdword-flags / Text-Dword-Flags contains the following: · func-top-flag: If set, the annotated machine word is the top of a function.
[0196] Data-Dword-Metadata / Data-Dword-Meta-Datum / Data-dword-flags / Data-Dword-Flags contains the following: public-flag, · writable-flag: If set, the associated machine word can be written (otherwise it can only be read). written-flag: if set, the associated machine word is written (because this flag was last cleared, e.g., when the object containing this machine word was last allocated). If memory with an unwritten flag is read, the result depends on the read-unwritten-mem-policy. ref-flag: If set, the associated machine word becomes a legal pointer / reference (ref).
[0197] Such bit arrays are also a simple and proven mechanism for annotation; see the Cambridge lowRISC project [low-risc], which does exactly this. Caching such tags as a function of the addresses they annotate is a solved problem, so prior art would work.
[0198] Function-Metadata Header: Every function has a Function-Metadata header that contains annotations for that function. The hard object provides a way to map from the function start address to the function's Function-Metadata. We call this map function-start-to-function-metadata-map. In one embodiment, a function's Function-Metadata is annotated by placing the Function-Metadata directly above the function as a Function-Metadata header. In this embodiment, to find the Function-Metadata for a function whose function start we know, we subtract the size of the Function-Metadata from the function-start and perform the load. This simple header embedding technique in abstraction performs the function-start-to-function-metadata-map function. In another embodiment, there may be a function-start-to-function-metadata-map that maps the current-function-start to the Function-Metadata of the function. Some embodiments of this map may be a prior art red-black tree, a prior art hash table, or a prior art skip list.
[0199] A hard object always knows a pointer to the current function, the function-start (the first instruction of the function) of the current function (current-function-start). When a function call is made, a pointer to the start of the function is known (because the call / jal / jalr instruction jumps to it), so the hard object stores the target function pointer in current-function-start. When returning to a target address within the target function, the return pointer metadata, along with the Text-Page-Metadata annotated to the target address, are used to reconstruct the current-function-start, as detailed elsewhere. Other forms of control transfer do not require modifying current-function-start or current-function-metadata, because they cannot transfer control to another function.
[0200] After a control transfer, or whenever the Function-Metadata of a function is needed, the function-start of the function in question (or current-function-start for the current function) is used to look up the Function-Metadata of the current function using the function-start-to-function-metadata-map. Furthermore, the result of this lookup can be cached in the current-function-metadata register.
[0201] Functions have versions (or refable-versions), and function or return pointers have time addresses, which operate similarly to similar metadata for heap / global data pointers. When a function is called through a function pointer or returned through a return pointer, the hard object will fault if the function version does not match the pointer's time address. One use of this functionality is to prevent call-after-free or return-after-free (to a function) in the face of dynamic loading / linking. That is, you can load or unload a dynamically loadable ELF, then stop any outstanding old function / return pointers to that function, and allow function calls / returns through them. Function-Metadata (function-Meta-Datum) contains: · mod-owner: The module-owner-id of the associated function, which includes (1) mod-owner-id: The module-owner-id of the associated function, (2) mod-owner-suff-len: The module-owner-suffix-length of the associated function, · danger-flag: If set, the code of the associated function has dangerous permissions. · may-call-suff-len: Restricts calls to this function to the module group. may-make-ref-suff-len: Restrict references to this function to the module group. · Function-length: The length of the sequence of instructions in the associated function. Function references are possible (see below), where the function references available include: require-function-pointer-flag: If set, this function must be called through a formal function pointer; otherwise, it does not need to be called through a formal function pointer. · refable-version: The version of this function. · refable-version-ceiling: Maintain the invariant that there are no outstanding formal pointers to the function with a time greater than version and less than version-ceiling. · refable-version-next-ceiling: Useful during concurrent version collection to record the current function version at the start, so that the value can be used to place the next ceiling at the end (i.e., during collection, the function version may change because the program is still running; for example, if a dynamically loaded library is unloaded and another one is loaded, only the initial version can be used as the last ceiling).
[0202] In one embodiment, the above fields use the following bits: · mod-owner: 19bit, danger-flag: 1 bit, may-call-suff-len: 4 bits, may-make-ref-suff-len: 4 bits, Length: 26 bits, Function reference possible, 10 bits, Here, in the embodiment, the function reference possible includes the following: · require-function-pointer-flag: 1 bit, refable-version: 3 bits, refable-version-ceiling: 3 bits, ·refable-version-next-ceiling: 3 bits.
[0203] Function-Metadata public-flag can be implemented with may-call-suff-len of 0 for private and 15 for public. Function call / return time constraints: A call through a function pointer that transfers control to a function, or a return through a return pointer that transfers control to a function, Through a function pointer or return pointer, The time address of a function pointer or return pointer must match the refable-version of the function to which control is being transferred, otherwise the hard object will generate a fault.
[0204] The hard object annotates each function pointer with that function's Function-Metadata header, so that a single embedding places this Function-Metadata header immediately before the start of the function and makes it easy to find the start of the function from a pointer using header size and cost subtraction. This map from function pointers to Function-Metadata headers can of course be cached in the standard way (using the low-order bits of the function pointer as the cache index). Caching such Function-Metadata as a function of the addresses they annotate is a solved problem, and thus prior techniques would work. Furthermore, however, because most references to this map require the current function's metadata, the hard object could also cache the current function's map entry in dedicated current-function-metadata registers (current-danger-flag, current-function-start, current-function-end) just for that purpose, effectively providing a second layer of caching. This appears to be very effective in practice.
[0205] Table Mapping IDs to Metadata Hard objects annotate metadata to objects and subobjects by annotating the obj-id and subobj-id fields with a structured Abs-Ptr that is used as an index to look up the object and subobject metadata in the table, as follows: The hard object uses a page-class-id-map to map each page-class-id to a Page-Class-Meta-Datum. One embodiment of this map is a table that maps page-class-ids to pointers to Page-Class-Meta-Datums. Another embodiment is that the page-class-id itself is a pointer to a Page-Class-Meta-Datum. The Page-Class-Metadata (Page-Class-Meta-Datum) contains: page-class-alloc: The allocator that manages this page class. · page-class-alloc-ref-flag: Reference flag for pointer to allocator. next-obj-id: the next unused object ID of this page class, · map-obj-metadata-table: The table of Object-Metadata for objects of this page class. · map-subobj-metadata-table: Table of Sub-Object-Metadata for objects of this page class.
[0206] In one embodiment, the above fields use the following bits: page-class-alloc: 36 bits (dword-aligned pointer with the lowest 3 bits set to 0), page-class-alloc-ref-flag: 1 bit, next-obj-id: 9 bits, map-obj-metadata-table: 36 bits (dword-aligned pointer with the lowest 3 bits set to 0): pointer to the table of Object-Metadata for objects of this page class (note that in the prior art C programming language, it is common to use the language in a way that integrates tables with pointers to identification / tables, and a detailed description of this will do so when convenient), map-subobj-metadata-table: 36 bits (dword-aligned pointer with low 3 bits of 0): Pointer to the table of Sub-Object-Metadata for objects of this Page Class (note that in the prior art C programming language it is common to use the language in a way that integrates tables with pointers to identification / tables, and a detailed description of this will do so when convenient).
[0207] Each object has associated Object-Metadata. Object-Metadata (Object-Meta-Datum, object-metadata, object-meta-datum, object-metadatum) contains: has-subobj-metadata-flag: Whether this object has subobject metadata, iterator-flag: whether the inappropriate subobject is an iterator, may-make-ref-suff-len: Restrict references to this object to the module group, may-read-suff-len: Restrict reading of this object to module-group, may-write-suff-len: Restricts writing to this object's module-group, writable-flag: If cleared, data associated with the object may not be written. mod-ownable: the mod-ownable-id of this object, integrity-flag: The integrity flag of this object, · Referable (see below), · Start (object-start): A pointer to the start of the object. · length (object-length): the length of the object, Here, the possible references include: refable-owner-managed-flag: If set, the owner can place a targetable refable-version of the same object as refable-informally-targetable-flag. Otherwise, only dangerous code can do so. refable-may-make-ref-flag: If set, non-dangerous code may create references to this object. Otherwise, only dangerous code can create references to this object. refable-informally-targetable-flag: If set, this object can be accessed via an informal pointer. Otherwise, an official pointer is required to access this object. refable-version: the version of this object; when accessing data through a pointer ("deref"), the time address of the reference must match the refable-version of the target object. · refable-version-ceiling: Maintain the invariant that there are no outstanding formal pointers to objects with a time greater than the version and less than the version-ceiling. refable-version-next-ceiling: Useful during concurrent version collection to record the current object version at the start, so that the value can be used to place the next ceiling at the end (i.e., during collection, the program is still running, so the object version may change, and only the initial version can be used as the ceiling at the end).
[0208] The object-end is calculated as the sum of the object-start and object-length. In one embodiment, the above fields use the following bits and fit into two double words, 128 bits: has-subobj-metadata-flag: 1 bit, ·iterator-flag: 1 bit, may-make-ref-suff-len: 4 bits, may-read-suff-len: 4 bits, may-write-suff-len: 4 bits, ·writable-flag: 1 bit, Mod-ownable: 15-bit, integrity-flag: 1 bit, · Referenceable (see below): 18 bits, Start: 39 bits, Length: 39 bits, Here, in the above embodiment, the referable includes the following: refable-owner-managed-flag: 1 bit, refable-may-make-ref-flag: 1 bit, refable-informally-targetable-flag: 1 bit, refable-version: 5 bits, refable-version-ceiling: 5 bits, and ·refable-version-next-ceiling: 5 bits.
[0209] Each object (or its object-metadata) has an associated Sub-Object-Metadata-Table. The Sub-Object-Metadata-Table (sub-object-metadata-table) contains: map-subobj-metadata: Maps from sub-object IDs to Sub-Object-Metadata-Mem-s, map-subobj-topid-to-subobj-id (map-subobj-topid-to-absolute-subobj-id): Maps from the top ID to the subobject ID. num-abs-subobj-ids: number of subobject IDs in this table, num-subobj-topids: The number of top IDs in this table. contains-a-non-immediate-non-trivial-union-flag: whether the C type tree corresponding to this table contains a non-immediate non-trivial union subtype, and · top-is-array-flag: Whether the top subobject in the subobject tree is an array.
[0210] Sub-Object-Metadata-Mem (sub-object-metadata-mem, sub-object-metadatum-mem), which is a representation in a table in the metadata memory, contains the following: offset-from-object-start (subobject-offset-from-object-start): The start of this subobject expressed as an offset from the start of its associated object. · Length (subobject-length): The length of the subobject, is-array-flag: whether the C type corresponding to this subobject is an array, iterator-flag: Whether this subobject is an iterator, and · parent-subobj-id: The subobject ID of the parent subobject.
[0211] The cached representation, Sub-Object-Metadata (sub-object-metadatum, sub-object-metadata), includes: improper-flag: whether this subobject is an improper subobject, iterator-flag: whether this subobject is an iterator, start (subobject-start): pointer to the start of the subobject, and · Length (subobject-length): The length of the subobject.
[0212] The subobject-end is calculated as the sum of the subobject-start and subobject-length. Absolute subobject IDs (absolute-sub-object-id-s) are numbered depth-first from the top of the subobject tree. However, they can use many bits. Below are some compression algorithms for representing absolute subobject IDs in pointers while using fewer bits than would be required in a naive encoding. Hard objects can represent absolute subobject IDs of subobjects that start on a page and end on the same page or on the next page by representing the absolute subobject ID as the sum of (a) the data page metadata page-subobj-id-abs-base and (b) the pointer subobj-id; that is, given a pointer where the pointer is subobj-id-namespace=bottom-SIDN, find the absolute subobject ID of the desired subobject as follows: 1. If the pointer has page-overflow-flag set, use the previous page instead of the page pointed to by the pointer data-page-index (i.e., subtract 1 from data-page-index before searching for the page); 2. For the page pointed to by data-page-index, find the data page metadata page-subobj-id-abs-base. 3. Add the subobject id of the pointer to page-subobj-id-abs-base to get the absolute subobject id.
[0213] A hard object can represent the subobject ID of a subobject at the top of the subobject tree by numbering the subobjects breadth-first while descending the subobject tree up to a point (such as when the available topids are exhausted), and then recording a mapping from topids to absolute subobject IDs in a map-subobj-topid-to-subobj-id table (typically mirroring a C-type tree). That is, given a pointer where subobj-id-namespace=top-SIDN, find the absolute subobject ID of the desired subobject as follows: 1. Interpret the subobject ID of the pointer as a topid (subobj-topid). 2. Obtain the absolute sub-object id by searching the map-subobj-topid-to-subobj-id in the object's Sub-Object-Metadata-Table.
[0214] To find the object and sub-object metadata for a specific Abs-Ptr: (1) From the data-page-index (the part of the pointer to the beginning of the page), look up the data-page meta-datum (in one embodiment, by finding it in the data page table entry (data PTE) if extended with hard object metadata). If the pointer has the page-overflow-flag set, look for the page-class-id of the previous page, i.e. data-page-index minus 1. A way to do this without searching multiple Page Table Entries (PTEs) is in each PTE, and remember the page-class-id of the previous page as well. This only needs to be done for PTEs in the Translation Lookaside Buffer (TLB), not the actual page table, because a TLB cache fill can look up two adjacent PTEs and use them to fill a single cache entry. (2) Search for Page-Class-Metadata in the Page-Class-Metadata-Table from the page-class-id of the data-page meta-datum. (3) In parallel: ·In Page-Class-Metadata, find the map-obj-metadata-table and search for Object-Metadata from the obj-id (from the pointer metadata). ·In Page-Class-Metadata, find the map-subobj-metadata-table and search for the Sub-Object-Metadata-Mem from the subobj-id (from the pointer metadata).
[0215] Construct Sub-Object-Metadata from Sub-Object-Metadata-Mem and Object-Metadata by combining both pieces of information as follows: · improper-flag: True exactly if absolute subobject ID is 0. ·iterator-flag: Copies from iterator-flag of Sub-Object-Metadata-Mem. · Start (sub-object-start): Add the start of Object-Metadata and the offset-from-object-start of Sub-Object-Metadata-Mem. · Length (sub-object-length): Copy from the length of Sub-Object-Metadata-Mem.
[0216] To do this in a simple way, you would need to access the object and subobject metadata cache after the TLB returns with the data PTE metadata. This means that in this implementation, hard objects must access two layers of serial cache, although it is generally more efficient to access the cache in parallel. However, this technique is also what you would need to do when implementing a caching strategy that caches physical memory (rather than virtual memory). This technique is described in Patterson's undergraduate textbook [patterson-hennessy-2nd-ed-1998, p. 595], so it seems likely that this technique is not prohibitively expensive or that the technique will not appear in textbooks. "Figure 7.27 assumes that all memory addresses are translated into physical addresses before accessing the cache.... In such a system, the time to access memory, assuming a cache hit, must accommodate both a TLB access and a cache access. Of course, these accesses can be pipelined."
[0217] Caching Object-Metadata is an interesting puzzle. Doing this effectively uses both the obj-id and the page-class-id in the cache index, since the obj-id only has meaning relative to the page-class-id. One implementation is to simply compute the bitwise XOR of these two fields (the low-order bits) of the pointer. Note that bitwise XOR is very fast in hardware, requiring only one layer of transistors.
[0218] Caching Sub-Object-Metadata is even more interesting. Note that the representation of Sub-Object-Metadata in the cache is different from the Sub-Object-Metadata-Mem in memory. Because the cached version has an absolute address, which is also a function of Object-Metadata, the cache must use both obj-id and subobj-id in calculating the index. Similarly, because both obj-id and subobj-id only have meaning relative to the page-class-id, and furthermore, because subobj-id only has meaning relative to the subobj-id-namespace, we effectively cache the Sub-Object-Metadata and effectively use obj-id, subobj-id, subobj-id-namespace, and page-class-id in the cache index. As mentioned above, one embodiment would be to simply compute the bitwise exclusive or of all these fields of the pointer (or their low-order bits), but given that subobj-id-namespace is only a single bit, we can just add it to the low-order bits of subobj-id, and since we only need three inputs, we only need a three-way bitwise exclusive or.
[0219] An alternative embodiment of the above caching scheme could conceivably avoid serial caching (searching page-class-id first, then Object-Metadata / Sub-Object-Metadata (in parallel)), but would no longer guarantee a unique representation of metadata in the cache, increasing cache pressure and requiring cache flushes when metadata changes. If this embodiment is used, replace page-class-id with page-index in the caching discussion above regarding which fields of the pointers to use for the Object-Metadata cache index and the Sub-Object-Metadata index. Again, this representation would be redundant. The result would be that the Object-Metadata of a single object would be cached multiple times, and similarly for sub-objects.
[0220] Version Collector This subsection describes the version collection system as a whole. It is partially redundant with the above. A valid reference is one whose time address is equal to the refable-version of the target object it points to. A reference that is not valid is stale. Hard objects prevent stale references from being dereferenced, but the refable-version of the object cannot be reused, although the stale reference is still outstanding somewhere. Therefore, the hard object system wants to find stale references and clear them by clearing the ref-flags. Doing this is the purpose of version collection.
[0221] When considering references during version gathering, keep in mind that all references point from a source address to a target address.
[0222] Page Participation Path: Annotate each page with the page-version-collection-obj-scan-live-flag (one embodiment is to use a bit array). If this flag is set, this detailed description indicates that the objects on the page corresponding to the flag are "participating in the object path" below. For example, a useful configuration at the beginning of a scan might be: all memory-mapped data pages have page-version-collection-obj-scan-live-flag set, and non-memory-mapped data pages (or their metadata pages are not memory-mapped, see below) have that flag cleared. Another embodiment could determine for each heap page whether it is hot enough (frequently used) or hopeless (its objects are short enough relative to the available unused versions) to be worth scanning.
[0223] For purposes of version collection, all that is really needed for a data page is the metadata of the objects stored on that page. That metadata for objects stored on a data page may be (a) stored on the same data page as the object, or (b) stored on a different but corresponding metadata page. In the case of (b), if said metadata for the page's objects is in a separate metadata page corresponding to said data page, then throughout this detailed description, the phrase "if a data page is memory-mapped" means that its corresponding metadata page (containing the metadata for the objects on said data page) is memory-mapped. A page-version-collection-obj-scan-live-flag corresponding to a data page is also considered to be annotated with the metadata corresponding to the objects on the data page. If a data object spans multiple data pages, it is considered for annotation purposes to be in one of those pages, such as the first page.
[0224] Reference path: Scan all legitimate pointers (i.e., scan their source addresses): registers, and · Memory: global, stack, heap.
[0225] For each reference in the above locations, check to see if the reference is stale: 1. Look up the page-version-collection-obj-scan-live-flag of the target page of the reference. 2. If the page-version-collection-obj-scan-live-flag is set for the page, check if the page is still mapped in memory: (a) If the page is not yet mapped, then clear the page-version-collection-obj-scan-live-flag for the page, since the page is no longer participating in the scan, and perform the scan taking this reference into account, performing this reference check (an alternative embodiment could load the page into memory and continue below). (b) If the page is still mapped, continue below. 3. Check whether the formal pointer is still valid by checking whether the following two values are equal: (a) the time address of the reference and (b) the refable-version of the target object. If they are equal, the reference is valid; if not, it is not (the reference is stale). 4. If the reference is not valid (stale), make the reference inoperable, for example by clearing its ref-flag.
[0226] Instead of clearing the ref-flag in the last step, we can also place the next-ceil annotated on the target object in the current reference version. Doing this ensures that the current old version is not reused. This embodiment can be useful in situations where immediately clearing the ref-flag is somehow expensive. The above check can also be interrupt-driven, and its use is demonstrated below.
[0227] Object path: Scan for objects on the page that have page-version-collection-obj-scan-live-flag set, and for each object, update the version clock for that object as follows: 1. Place version-ceiling:=version-next-ceiling. 2. Place version-next-ceiling:=current-version.
[0228] When the memory allocator processes a call to free() on a pointer to an object, it increments the object's current-version. The increment is modular, meaning that if the increment results in a number too large to be represented in the number of allocated bits, the number is set to 0. When the memory allocator processes a request to allocate memory, such as a call to malloc(), it will not reuse a deallocated object if current-version=version-ceiling for that object.
[0229] Collecting versions concurrently with the execution of the user program: To complete the concurrent execution of this algorithm, we need to handle copying formal pointers from checked ranges to unchecked ranges. We maintain a scan-color, each container of formal pointers. Annotate each data page with three values: no at all, some, and yes to all. Annotate each register with two values (no, yes).
[0230] Scan the page and mark it with a color to indicate that it has been scanned. 1. When I start the reference scan, all the pages start out with absolutely no color. 2. When you start scanning a page, color in some of it. 3. Once you have finished scanning the pages, color in "All Yes."
[0231] When a formal pointer is copied, if we are copying from a no register to a page with some or all yes, we stop and check the pointer as above during the reference scan, and clear its ref-flag if it is stale. We do this as follows: When loading a page-to-register reference, the register takes on the color of the target page. Load from page no at all or page part, enter register color no, Load all from page yes means set register color yes. Storing a reference to a page from a register: If the register color is No and some or all of the page colors are Yes, see "Handling Broken Reference Transfers" below. Otherwise, if the register color is a combination of other colors, only allow storage.
[0232] Handle broken reference transfers. If the register color is No and some or all of the page colors are Yes, then one of the following is true: 1. Induce an interrupt-driven liveness / staleness check (detailed above) to see if the reference is stale, then see "Case (1)" below, or 2. (Optimization) Allow the store by simply clearing the page-version-collection-obj-scan-live-flag for the page the reference points to. Doing this is an optimization that reduces the cost of interruption by eliminating the need to look up the metadata for the object the reference points to.
[0233] In the case of (1) (above), when an interrupt-driven liveness / staleness check is performed, the following occurs: (a) If the reference is not out of date, color the register "yes" and allow storage. (b) If the reference is stale, clear the ref-flag of the register reference before performing the store.
[0234] Optimization: Toggle whether 0 means no and 1 means yes (or vice versa) for every scan: Naively done, this would require another scan to reset the page container's yes annotation to no at the start of each scan. Instead, toggle whether 0 means no and 1 means yes (or vice versa) for every scan.
[0235] Optimization: Do not swap pages in any order during reference scan: During the ref-scan-phase, checking if a reference is out of date requires reading the Object-Meta-Datum annotated to the target object to get its refable-version. Simply doing this incurs an arbitrary paging-in of the Object-Meta-Datum. It is much faster to perform the ref-scan-phase of version collection without incurring this arbitrary paging. Check the target of a reference only if: The target page that the reference points to has the page-version-collection-obj-scan-live-flag (participates in object scan), The target page is still resident in memory.
[0236] If the first condition is true and the second condition is not true, clear the page-version-collection-obj-scan-live-flag for this page (this is an optimization, not paging). This page will not participate in the next object scan.
[0237] Optimization: Best-effort reference pass: Interrupt-driven checking of copied references from unchecked containers to checked containers makes it faster and more predictable reducing the number of pauses. There is no need to perform an object pass immediately after performing a reference pass. The version collector can be configured to perform a best-effort reference pass when version collection does not use the techniques described above to prevent copying references from unchecked containers to checked containers. Therefore, during the best-effort lookup pass, references may have been copied from an unscanned container to a scanned container, so it is not safe to perform an object pass after the best-effort lookup pass. However, the number of outstanding stale references can be significantly reduced by a best-effort reference scan, so the number of pauses due to interrupt-driven checking of copied references from unchecked containers to checked containers can be fewer (this is an optimization) if a second guaranteed (non-best-effort) reference scan is performed immediately afterwards.
[0238] Optimization: Scan using client program data flow: An interrupt-driven check occurs when the program data flow goes in the opposite direction to the version collection scan. Therefore, if the version collection pass scans memory in roughly the same direction that the program tends to write data, interrupts are reduced. To do this, the scan needs to know the program's data flow. Possibly obtained using machine learning of data access trace logs generated from program execution. Perhaps the client code could somehow communicate that directly to the scan, similar to how client programs can currently provide hints to the mmap system call. · Perhaps the compiler can infer it using static analysis.
[0239] Hard Object Operators: Hard objects provide operators for programs and / or kernel and / or operating system libraries that affect the behavior of hard objects. Hard object operators include the operators listed in this section. Some of these operations, such as most of the operations that allow metadata modification, are clearly dangerous for user code to be able to perform. However, it is fairly clear which are dangerous, so this section only comments on those that may require some insight. These hard object operators can be implemented as additional instructions in the instruction set, or By a hardware-intercepted system call (called ecall in RISC-V®), or In some architectures (such as RISC-V®), by exposing dedicated registers (called Control and Status Registers in RISC-V®) that can be read and written using dedicated Control and Status Register access instructions, or By being implemented in software as a system call added to a hard-object-aware kernel that has access to another operator that produces the desired result or can directly modify metadata memory, or · Perhaps by other means.
[0240] This subsection lists some proposed hard object operators that have proven sufficient for a single software simulation of a hard object. Those skilled in the art know that every type of metadata annotated to every type of thing must have at least getter / putter operators, and hard objects are no exception. Therefore, this detailed description does not necessarily exhaustively list all such operators and perhaps leaves some hints. Similarly, those skilled in the art know that the maps / tables / annotations / data structures disclosed herein must have some way to initialize, read, and write (collectively "manage"). The interesting details are only related to choices made by the specific choices of the embodiment / implementation. Therefore, this detailed description does not necessarily exhaustively list these map / table / annotation / data structure management instructions / operators and perhaps leaves some hints. Operators with the prefix "idem" are idempotent. If the input is already how the operator does its output, the operator does nothing. Throughout this detailed description, "get" means reading a value, "put" means writing a value, "set" means setting a flag to true, and "clear" means setting a flag to false. Furthermore, when referring to the state of a flag, when a flag is described as "set" it means its value is true, and when a flag is described as "clear" it means its value is false.
[0241] Hard Object Data Operators: These are additional operators required when manipulating user data.
[0242] Non-consistent loads / stores: If data is annotated with a distinct consistency flag, then normal load / store instructions / operators cannot access the object. Instead, only dedicated non-consistent load / store instructions can access it (and they cannot be used accidentally). Furthermore, these non-consistent instructions / operators cannot access data annotated with a set consistency flag; for those, use normal load / store instructions. load_noninteg_byte, load_noninteg_half, load_noninteg_word, load_noninteg_double; store_noninteg_byte, store_noninteg_half, store_noninteg_word, store_noninteg_double.
[0243] Dewdrop implements these inconsistent load / store operators as a sequence of instructions rather than as a single hardware instruction. Doing this avoids the need to create new load / store instructions. To do this, Dewdrop · Enumeration type MemAccessInteg{no_MemAccessInteg=0, yes_MemAccessInteg=1,} and its enumeration instance next_instruction_mem_access_integ, · Flag boolean next_instruction_mem_access_integ_just_put, and · Hardware implemented Dewdrop ecall (described elsewhere) clear_next_instruction_mem_access_integ.
[0244] clear_next_instruction_mem_access_integDewdrop ecall is called Then, The next_instruction_mem_access_integ_just_put flag is set to true and next_instruction_mem_access_integ is placed in no_MemAccessInteg. During observe_instruction_end, if next_instruction_mem_access_integ_just_put is true, next_instruction_mem_access_integ_just_put is cleared to false, otherwise next_instruction_mem_access_integ is set to yes_MemAccessInteg. As a result, for one subsequent instruction, the value of next_instruction_mem_access_integ becomes no_MemAccessInteg. In this case, normal memory access instructions (load / store) cannot access objects with the consistency flag set, but can access objects with the consistency flag cleared.
[0245] Generic CSR: These operators manage a generic control status register (CSR) that can be used by hard objects to pass additional arguments or modify user code when an additional scratch register is needed but you want to avoid using a general-purpose (int) register. This situation often arises when modifying assembly when the register roles have already been assigned by the compiler. get_hard_object_arg1_csr, put_hard_object_arg1_csr; get_hard_object_arg2_csr, put_hard_object_arg2_csr; get_hard_object_scratch1_csr, put_hard_object_scratch1_csr; get_hard_object_scratch2_csr, put_hard_object_scratch2_csr.
[0246] Hard Object Metadata Operators: These are additional operators required when manipulating user data and metadata annotated to text. This section attempts to split them into useful categories, but note that categories are a bit of a judgment call.
[0247] Annotation: These operators manage metadata annotations. get_kind_of_structured: Gets the Ptr-Kind-Enum (and Abs-Ptr-Kind-Encoding, if relevant) from a structured pointer. put_flags_for_16_dwords: Puts 16 machine words (in this embodiment, each 64-bit double word in RISC-V terminology) of Text / Data Dword-Meta-Datum flags at a time. This is particularly efficient because the hard object annotates 4 bits of each dword, 64 bits in this embodiment (64 / 4 = 16 / 1 ratio). It is convenient to make the metadata memory the same width as the data memory, so writing one dword of metadata writes 64 bits. This is equivalent to writing 16 data dwords of metadata at once. This instruction is particularly useful for setting an entire page of flags very quickly. narrow_structured operators: These take as input a target pointer (abs-ptr, stack-pointer, or stack-object-ptr) and other data such as the new object size (new-object-size), and output an abs-ptr or stack-obj-ptr “narrowed” to point to a subobject of that object with the requested object base pointer and size. narrow_structured_immediate: Takes a pointer and expects the requested subobject to be representable as an immediate. If the requested object cannot be represented as an immediate subobject, a failure occurs. narrow_structured_subobj_id_delta: Takes a pointer and a sub-object-id delta, the difference between the input sub-object-id and the output sub-object-id. Using sub-object-id deltas can prove very useful, as if the original program were written in the C programming language. A programmer can easily find themselves in a situation where they want to narrow a pointer to a struct B to a pointer to one of its elements, a long int y, represented as follows: struct B{long int x;long int y;}; However, hard objects do not know whether a pointer to struct B is standalone or points to a struct B embedded within a larger struct A, as follows: struct A{long int q;struct B b;long int r;}; By using subobj-id deltas, this operator can make a struct B subobject subtree isomorphic to a standalone struct B subobject subtree, allowing both of these situations to be handled in the same way. narrow_structured_table_search: Given an object base pointer and size, search the subobject table to find the subobject ID to use. This is necessary in some ambiguous situations. widen_structured_to_improper_subobj: Performs the inverse of the narrow operator above: widens a subobject to an improper subobject (the root of the subobject tree, allowing access to the entire object). get_page_class_alloc, put_page_class_alloc: Gets / puts the allocator associated with a data page. get_start_of_range: Gets a pointer to the start of the accessible range from a pointer somewhere within the range. get_is_iterator: Gets the iterator flag of a pointer. annotate_text_page:Annotates a text page with the argument metadata. addr_is_text: Returns whether the address points to text memory or something else. annotate_object: Annotates metadata on a heap or global object. Might be implemented in software as a system call that modifies the associated annotation, either by directly modifying the metadata or by using putter / getter hard object metadata operators on fields not listed here. Those skilled in the art would consider this to imply that all data or metadata fields require getter / getter operators.
[0248] Boundary: These operators manage boundary metadata annotations. set_ok_to_call_flag(set-ok-to-call-flag): Specify a register ID and its value as arguments, and if it is okay to call the value, set the ok-to-call-flag for that register. set_ok_to_return_flag (set-ok-to-return-flag): Specifies a register ID and its value as arguments, and if it is okay for the value to be returned, sets the ok-to-return-flag for that register. put_num_int_args_in_registers (put-num-int-args-in-registers), put_num_float_args_in_registers (put-num-float-args-in-registers): Places the num-int-args-in-registers or num-float-args-in-registers registers, respectively. clear_caller_save_reg_except_args: Clears the written-flag and ref-flag of the caller-save registers except those specified as arguments by ok-to-call-flag-s or num-int-args-in-registers and num-float-args-registers. get_object_size_of_obj, put_object_size_of_obj: Gets / puts the object size of an object. get_stack_base_ptr: Returns stack-base-ptr. get_text_dword_kind_of_text_addr, put_text_dword_kind_of_text_addr: Gets / puts the Text-Dword-Flags (meaning func-top-flag) at the specified text address. erase_data_dword_metadata_for_raw: Erases Data-Dword-Flags annotated to a data dword. get_stack_floor_ptr, put_stack_floor_ptr: Gets / puts the stack floor register. assert_user_current_danger_flag: Faults unless the user program counter points within a function annotated with danger_flag.
[0249] Modularity: These operators manage modularity metadata annotations. get_current_mod_owner: Returns the value of the current-mod-owner register. get_caller_mod_owner: Returns the value of the caller-mod-owner register. assert_caller_is_current_mod_owner: Asserts that the value of the current-mod-owner register is the same as the value of the caller-mod-owner register. transfer_obj_to_new_owner: Note that this is one of the few hard object operators that changes the metadata of an object, and can be used by code without dangerous privileges. The mod-ownable-id annotated to the object matches mod-owner except for the rightmost bits up to the length of mod-owner-suff-len in the text annotated with mod-owner, i.e., the code that owns the object can call this instruction to change mod-ownable-id to the ID of another module. This operator: Changes the mod-ownable annotated on the object to the new one specified in the argument, It also lowers the integrity-flag of the object in question, so the new owner cannot access it using normal load / store operations until the new owner sets the integrity-flag. get_integrity_flag_of_obj, put_integrity_flag_of_obj: Get / put the integrity flag annotated to the object. put_public_flag_for_data_dword, put_writable_flag_for_data_dword, put_public_writable_flags_for_data_dword, get_written_flag_for_data_dword, set_written_flag_for_data_dword, clear_written_flag_for_data_dword, get_public_flag_for_data_dword, get_writable_flag_for_data_dword, put_all_flags_for_data_dword: Gets / puts the respective metadata annotated to the data dword. get_mod_ownable_of_obj: Gets the mod-ownable annotated to an object. get_mod_suffix_of_obj, put_mod_suffix_of_obj: Get / put the mod-ownable suffix annotated to an object with respect to the specified or implied mod-owner-suff-len. get_default_mod_ownable_for_mod_owner: If mod-ownable is specified, get mod-owner with suffix zero. get_mod_owner_of_function: Get the mod-owner annotated to a function. get_may_read_suff_len_of_obj, put_may_read_suff_len_of_obj: Get / put the may-read-suff-len annotated to an object. get_may_write_suff_len_of_obj, put_may_write_suff_len_of_obj: Get / put the may-write-suff-len annotated to an object. get_may_make_ref_suff_len_of_obj, put_may_make_ref_suff_len_of_obj: Get / put the may-make-ref-suff-len annotated to an object. get_writable_flag_of_obj, put_writable_flag_of_obj;Gets / puts the writable-flag annotated to the object.
[0250] Reference: These operators manage reference metadata annotations. get_refable_owner_managed_flag_of_obj, put_refable_owner_managed_flag_of_obj, put_refable_may_make_ref_flag_of_obj, get_refable_may_make_ref_flag_of_obj, get_refable_informally_targetable_flag_of_obj, put_refable_informally_targetable_flag_of_obj: Get / put problem metadata annotated on an object. get_refable_version_of_obj, put_refable_version_of_obj: Get / put problem metadata annotated to an object. inc_refable_version_of_obj: Increments the refable-version of an object. Recall that the recommended strategy for memory allocators is to increment the refable-version of an object when it is deallocated (in a free() handler). If the refable-version of this object cannot be incremented because the new version is equal to the refable-version-ceiling, a failure occurs. A correctly-written memory allocator should never get into this state; see below; If the next refable version of the new version cannot be incremented because the new version is equal to refable-version-ceiling, return false. In this case, the memory allocator should consider this object as dead memory and not place it on the free object list to be used again to service new object requests (such as calls to malloc()). Otherwise, return true, in which case the memory allocator is free to place the deallocated object on the free object list. get_ref_flag_for_data_dword, clear_ref_flag_for_dword: Gets / puts problem metadata annotated to an object. make_abs_structured_into_abs_ref (make-reference command): Makes a structured Abs-Ptr into a formal Abs-Ptr. make_structured_have_obj_id: Places annotated obj-id into a structured (formal or not) Abs-Ptr. idem_make_func_raw_or_structured_into_forward_text_ref: Makes a raw function pointer or structured forward-text-pointer into a legal Forward-Text-Ptr. idem_make_structured_into_raw, make_ref_into_structured, make_ref_into_raw: Modify raw, structured, and formal pointers as indicated. get_ref_flag_of_structured: Gets the ref_flag annotated to a structured pointer. get_time_addr_of_structured: Gets the time address annotated to a structured pointer, if any. make_structured_have_time_addr_of_obj_version: Returns the specified structured pointer after annotating it with the same time address as the refable-version of the object it points to. make_perm_ref_into_ephem_ref, idem_make_perm_ref_into_ephem_ref: Decay a persistent pointer into a temporary one. idem_put_public_target_flag, idem_put_writable_target_flag: Returns the specified pointer with its public-target-flag / writable-flag (respectively) updated to the specified value.
[0251] Version Collection: These operators manage version collection. get_page_class_iter, get_data_page_iter, get_object_iter: Obtains an iterator for each named container, which can be used to obtain an iterator for the next container in the sequence. Note that with this technique of iterating through memory, the iterator is held by the operating system, so there can only be one at a time unless something is changed, such as the iterator being held in thread-local memory, a table of iterators being maintained and the operator providing an index to select one, or some other design being used. obj_version_ceiling_circ_minus_obj_version: Returns the version-ceiling of the object minus the object-version, but the subtraction is done "cyclically", i.e., the number of times the object version can be arithmetically incremented modulo 2 to the power of the number of bits in the object version field before it equals the object version ceiling (note that this is not just modular subtraction). obj_rotate_version_clock: 1.Place object.version-ceiling:=object.version-next-ceiling. 2.Place object.version-next-ceiling:=object.version. in_place_make_reg_abs_ref_into_structured_unless_fresh, in_place_make_mem_abs_ref_into_structured_unless_fresh: In both cases, we consider a reference (a formal pointer) that is in a register or in memory, respectively. If it is old (not new), i.e., the time address annotated to the reference is not equal to the refable-version annotated to the object it points to, we clear the ref-flag annotated to the reference, which makes it non-formal, i.e., a structured pointer. These operators are used in the ref-scan-phase of the version collector.
[0252] callee-save-reg-state: These operators manage the callee-save-reg-state. get_callee_save_active_flag, put_callee_save_active_flag: Gets / puts the callee-save-active-flag. When callee-save-active-flag is cleared, the callee-save-reg-state check is turned off. Doing this is necessary during the synchronous version collection ref-scan-phase when registers are being scanned. save_restore_callee_save_reg_state; Saves or restores callee-save-reg-state to or from the stack.
[0253] Hard Object Manager Operators: These operators manage the meta-state of hard objects. get_hard_object: Gets the version of the hard object that the hardware is running. get_hard_object_requested: This is an operator that allows the hard object library to read whether the user who started the process in which the hard object runs has the hard object turned on. This operator is naturally treated like a system call to get configuration from the kernel / operating system. init_hard_object: Initializes hard object registers from initial process state, such as the stack pointer. get_hard_object_active, put_hard_object_active: Gets / puts the hard-object-active-flag. If hard-object-active-flag is clear, hard objects may passively track metadata as it flows, but will not enforce many hard object properties. Marking a hard object as inactive can be useful during process startup, since the metadata has not yet been fully annotated to the object. It is useful to be able to switch to this inactive state on systems that retain prior art kernel mode, so that if the kernel is not set to run with hard objects enabled, the hard objects can be inactive while user processes running under hard object constraints are trapped in the kernel.
[0254] Alternative: Pivot-Centered Stack Objects Alternative embodiments of Stack-Obj-Ptr / stack-obj-pointer include: · Ptr-Kind-Enum; 3 bits; ·writable-target-flag; 1 bit; ·start-dnrelto-ptr-in-bytes;15 bits; ·size-in-bytes;15 bits; ·pivot-pow2-round-up; 5 bits; ·stack-obj-ptr; 24 bits; Construction: from stack-pointer, frame-pointer, and object-start: 1. Assert object-start >= stack-pointer. 2. Assert object-start < frame-pointer. 3. Set size := frame-pointer - stack-pointer. 4. Set pivot-pow2-round-up := get-log2-rounding-up(size). 5. Set get-frame-pivot-high-bits-mask := -(1UL << (pivot-pow2-round-up - 1)). 6. Set frame-pivot := (frame-pointer - 1) & get-frame-pivot-high-bits-mask. 7. Assert frame-pivot < frame-pointer. This is true because it starts less than frame-pointer and can only lower the value by masking bits. 8. Assert frame-pivot >= stack-pointer. This is true because the power of two is smaller than the size. Thus, masking off cannot subtract beyond the size. 9. Set frame-offset := truncate-to-num-bits(object-start - frame-pivot, pivot-pow2-round-up). This is the distance from frame-pivot to object-start, is signed, but truncated to the specified width and need not be larger than the power of two of the size. 10. Position stack-obj-ptr:=(frame-pivot<<1)|frame-offset; set field stack-obj-ptr. function: (a) get-frame-pivot-pointer: This always points to a valid address within the frame: 1.remove-offset-bits-mask=-(1UL< <pivot-pow2-round-up); 2.Return stack-limit+((stack-obj-ptr&remove-offset-bits-mask)>>1). (b)get-frame-pivot-offset: 1.get-offset-bits-mask=((1UL< <pivot-pow2-round-up)-1); 2.Return sign-extendstack-obj-ptr&get-offset-bits-mask. (c)get-pointer: 1.Return get-frame-pivot-pointer() + get-frame-pivot-offset(). (d) lives-at-least-as-long-as-another Stack-Obj-Ptr (Other): 1.Return get-frame-pivot-pointer()>=other.get-frame-pivot-pointer().
[0255] From the savings on the other metadata bits mentioned above, we can extend the size of the stack object to get 32KB.
[0256] Alternative embodiment: Object-centric virtual address space This embodiment brings a fundamental change to the concept of hard object virtual address space: instead of the current page-centric model that divides a 39-bit spatial address into a 27-bit page index and a 12-bit page offset, this embodiment proposes that the virtual address space should divide a 40-bit spatial address into a 5-bit obj-offset-suffix, a (35-(obj-offset-suffix+3))-bit object ID, and an obj-offset-suffix+3-bit object offset.
[0257] Because Object-Metadata encodes starting addresses in the physical address space rather than the virtual address space, a traditional PTE or TLB is not required. In this embodiment, doing so also eliminates mechanisms used to handle page boundaries, such as page overflow flags and page class IDs. Additionally, it frees up more metadata bits (by removing the object ID) while providing a larger overall addressable space.
[0258] However, note that in practice, when programmers write software, they often assume a linear memory model and use this assumption in their programs. For example, when writing concurrent programs, a standard technique for avoiding deadlocks is to acquire all locks needed for a transaction in an order that isomorphic to a global ordering, such as the memory addresses of the objects being locked. Also, when a programmer annotates one object with another, they often use a hash table to hash the addresses of the objects they are annotating. Both of these use cases (and probably others) require that objects have well-defined, easily accessible addresses in a linear address space. Therefore, it may be necessary to insert an additional layer of addressing to provide this linear ordering. Furthermore, hard objects may enable the construction of single-address-space operating systems. It is quite likely that people will want to build systems with hard object capabilities while also maintaining virtual memory address space separation. Therefore, directly mapping semantic objects to physical memory can be problematic, since address mappings between virtual address spaces can be interdependent without a separate virtual address layer between them. If a virtual address system lacks a simple page table mechanism to coordinate virtual-to-physical address mappings, then when moving data in physical memory (relative to its virtual address) during swapping, the process of updating all the metadata (here annotated in the object metadata) can be complex, to say the least. Even if done correctly, the only potential problem is the fact that it can take an unpredictable amount of time.
[0259] Both of the above concerns argue that, as a practical matter, it is likely necessary to insert a virtual address space layer into the designs in this section, replacing what is referred to in the remainder of this section as "physical memory" with a layer of "virtual memory" (with accompanying page tables, etc.), but doing so would not necessarily eliminate other potential benefits of the new memory organization disclosed in this section.
[0260] mechanism Creates a new Virtual-Obj-Ptr (a pointer through the virtual address space described by an object). A Virtual-Obj-Ptr (virtual-object-pointer) contains: Ptr-Kind-Enum (3 bits), Public target flag (1 bit), ·Writable target flag (1 bit), sub-object (19 bits, see below), space pointer (40 bits, see below), Here, the 19-bit sub-object includes: sub-object immediate flag (1 bit), The sub-object immediate structure includes the following: granularity (2 bits), immediate-dist-to-start (8 bits), immediate-length (8 bits), sub-object non-immediate structure, including: sub-object ID (18 bits); where the 40-bit space pointer contains: obj-offset-suffix (5 bits), Object ID (35-(obj-offset-suffix+3)) bits), Object offset ((obj-offset-suffix+3) bits).
[0261] Remove function-top-offset-in-dwords from Ret-And-Frame-Ptr / Return-Pointer. Add a 5-bit obj-offset-suffix to the space bits of both Ret-And-Frame-Ptr / Return-Pointer and Forward-Text-Ptr / Function-Pointer, and split the space bits into function-id and function-offset.
[0262] Append obj-offset-suffix to Forward-Text-Ptr.
[0263] Make the starting address of the Object-Metadata item a physical address instead of a virtual address. Make this address large enough to enumerate all physical addresses in memory.
[0264] The key of the Object-Metadata-Cache is the obj-offset-suffix and the object ID.
[0265] Creates 32 obj-metadata-for-size-start CSRs (Control Status Registers), which can only be read or set by code with dangerous privileges. Each contains a pointer to an indexable collection of object-metadata for each size (possibly a flat array, possibly a 3-level PTE-like hierarchy, etc.), or may be NULL if no memory is reserved for Object-Metadata of that size.
[0266] Creates a software Physical-Page-Table, which is system code used to coordinate which pages of physical memory are owned by which threads.
[0267] Delete the TLB.
[0268] Creates 32 system globals called object-id-frontier-for-size that contain the next unused object ID for each size.
[0269] Creates 32 system globals called object-id-global-free-list-head, which contain object IDs that are the heads of the global free list of valid objects or object metadata headers not owned by the allocator.
[0270] Rename make-pointer-into-ref to make-physical-pointer-into-virtual-ref, which takes as arguments a physical pointer, an obj-offset-suffix, an object ID, and a class-num.
[0271] Rename make-ref-into-pointer to make-ref-into-physical-pointer.
[0272] Convert the stack-limit-ptr register to contain a physical address instead of a virtual address.
[0273] No time addresses are required: two virtual objects with vastly different object IDs can be backed by the same physical address space without issue.
[0274] Object-Metadata Table (In this detailed description, the notation "x**y" means x raised to the power of y.) Instead of a page table, we have an Object-Metadata table. When we dereference a Virtual-Obj-Ptr, we look up the obj-offset-suffix and object ID of the pointer in the table.
[0275] Each obj-offset-suffix has an associated Object-ID-Frontier that represents the first unused object ID number of that size.
[0276] Similar to what is commonly done with page tables, a hierarchical table is used to provide the Object-Metadata table. This can be implemented as a two-level table, with the first level containing an index for the entire obj-offset-suffix and some bits of the object ID, with the remaining object ID used as the index. With the 37 bits potentially used for the suffix and object ID, this embodiment can split this into a 21-bit index for the first level (5 bits of obj-offset-suffix and 16 bits of object ID) and a 16-bit index for the second level. This implies a second-level array of Object-Metadata items of size 64K, or (assuming two-dword Object-Metadata) a 1MB array per second level of the table. While this appears to imply 2**21 entries at the top level, or a 16MB array in size, note that the index begins to underflow bits once the obj-offset-suffix reaches 16. Each obj-suffix-size halves the number of Object-Metadata items needed, so a suffix of 31 requires 1, so the underflow portion has 2**(32-16+1) or 2**17 entries, or 1MB. Because this underflow portion is the same size as one of the regular second-level entries, it can be treated as a separate entry, and the top-level array actually has (16*(1<<16)) or 2**20 entries, making it 8MB in size. Of course, the above is just one possible example structure (the size of the underflow entries has been chosen to be the same as the regular entries). Depending on your system requirements, it may be desirable to allocate more or fewer bits to each table level, or to provide more or fewer layers of hierarchy.
[0277] Note that in the two-level hierarchical table above, the upper levels of the table must be initialized, but the lower levels do not. Because object IDs are assigned sequentially and this embodiment tracks the frontier for each size, the cache fill mechanism checks the requested object ID against the frontier and fails if the ID is equal to or greater than the frontier. This means that the 1MB array that this embodiment defines for the second-level array can be filled very quickly, requiring only updates to the physical page table.
[0278] Once an object ID is used, it is rarely returned to the system. Normally, the Object-Metadata item is kept in the allocator that allocates objects of that size and can be reused. However, if an object ID is returned to the system, a free list can be kept per obj-offset-suffix for fast reuse. Of course, a version collection pass needs to be performed before the Object-Metadata item can be reused for another purpose.
[0279] While the two-level object metadata table above treats all possible object IDs equally, the object annotation mechanism stores objects sequentially, starting with 0. To avoid having to resort to hierarchical PTEs immediately, a flat array can be allocated for the smallest (and therefore most likely to be used) object IDs. Determining which table to perform the lookup in is as simple as a shift and compare. If this embodiment does this for the lowest 11 bits of the object ID, this creates an initial flat array of size 2**20 bytes (actually less, since this embodiment can prune unnecessary entries for larger sizes, which require fewer object IDs). 2048 objects per size is likely sufficient for most object sizes in most programs, and therefore this can be a good way to reduce the penalty of Object-Metadata cache misses. As above, this initial flat Object-Metadata array can provide more or fewer bits, depending on system requirements.
[0280] Annotating Objects To annotate an object located at a particular address, the following steps are required: 1. Round up the object-size to the next power of 2. This is called pow2-object-size. 2. Check if there is a non-null object ID on the free list for this object size. If there is, keep the current value, update the free list head, and go to step 4. Otherwise, continue. 3. Read the current value of Object-ID-Frontier into a variable named new-object-id and increment Object-ID-Frontier (this may need to be atomic in a multi-threaded system). 4. Assert that the Object-Metadata indicated by the new-object-id is consistent with a valid object or one not in use by the allocator. 5. Write the Object-Metadata fields and mark them as active. 6. Call make-physical-pointer-into-virtual-ref to create a reference to the new object. 7. Return the new reference.
[0281] Possession of physical space Before an object can be annotated, a range of memory in the physical address space must be reserved for that object. In the case of globals, the space is pre-reserved by the loader based on the data section of the ELF file. The allocator code, on the other hand, needs to reserve physical space for itself. The system can maintain a physical page table, which indicates, for each page of physical memory, whether it is allocated and (in a multi-threaded system) which thread owns it. The size of the pages in this page table entry is a system implementation detail and can be chosen by the system code based on the capabilities of the backing memory device.
[0282] Converting stack to physical address Accesses to the stack via Stack-Obj-Ptr can be converted to a physical address by offsetting from the value of stack-limit-ptr. This handles many stack accesses, but not accesses to the stack via a stack pointer (not Stack-Obj-Ptr).
[0283] To handle stack pointers, we annotate the entire 8MB range of the stack as a single object with dedicated stack Object-Metadata. We reference this object and offset it to the end of the object range. Now, one Object-Metadata can provide virtual to physical translation of offset stack pointers for the entire stack range.
[0284] Converting text to physical addresses All dynamic jumps are required to pass through the Forward-Text-Ptr.
[0285] Remove function-top-offset-in-dwords from Ret-And-Frame-Ptr. Represent text pointers with the same space bits as object pointers. Use 40 space bits to split into object ID and offset based on the suffix length value. Functions are considered a special kind of executable object. A bit in the object table payload indicates whether the object is a data object (and therefore not executable) or a text object (and therefore executable). Therefore, this embodiment moves the function header from the dword immediately before the start of the function to the object metadata table. If there is a miss in the function cache, the ID can be used to offset into this table to find the correct metadata header. Add the function's start-physical-address to the function header. Describe the function's length in bytes.
[0286] Because return and forward-jump references contain the full virtual text address, it is easy to use these references to find the function metadata for their target addresses. This is not as straightforward for static jumps, but can be achieved by modifying the behavior of the jal instruction based on its link register. In the case of ra, the immediate is considered to be in the function ID space. If x0, the immediate is assumed to be in short offset space unless a dedicated performing-tail-call CSR is set, in which case it is assumed to be in function ID space.
[0287] When offsetting in function / object ID space, the offset-suffix-length bits are considered the least significant bits of the object ID. This is different to how it is represented in pointers, but it offers some nice properties. This allows loaders to group multiple functions and objects of different sizes that appear together in the same translation unit, allowing them to be offset from each other in the ID space by a relatively small ID offset. This is useful for jal calls (which have a limited range of immediate offsets), or for representing interrelated text and data in position-independent code.
[0288] If the system is inserting unavoidable dynamic checks, the call graph checking code ensures that a jal performing a tail call cannot be executed separately from the CSR set performing-tail-call. Otherwise, the previous static jal results in a dynamic decision between two different static targets: one in the short offset space and one in the function ID offset space. If the system is not inserting these dynamic checks, there is no need to worry about this ambiguity. Because static jumps can only jump to the beginning of the same function or another public function, this choice in interpreting the jump instruction cannot be used to attack another function.
[0289] The performing-tail-call CSR can also be used to enable branch targets to perform tail calls, but this is not important since the GCC compiler does not appear to emit code to perform tail calls via branch instructions.
[0290] This embodiment allows functions to be called statically only at the beginning of a function, so the offset of the function is implicitly 0 upon completion of the call.
[0291] Sub-object processing Page-centric embodiments of virtual memory annotated sub-object information into page table entries. Object-centric embodiments do not have a page to annotate pointers to sub-object information for a class of object on a page. However, the reason page-centric embodiments annotated sub-object information per page rather than per object is because it would be inefficient to sequentially examine both the PTE and object metadata to fill a sub-object cache miss. In object-centric embodiments, access object metadata is created before other cached metadata, so it is preferable to place a pointer to the sub-object metadata in the object metadata.
[0292] In the new scheme, accesses to a Virtual-Obj-Ptr result in parallel accesses to the object cache and the sub-object cache, just as before. However, if there is a miss in the sub-object metadata, the sub-object cache loads the sub-object metadata pointer from the object metadata and retrieves the appropriate sub-object meta-datum from there. This may make some sub-object cache fills slower compared to object cache fills, but it requires the same number of dependent metadata accesses as in the old implementation (old scheme: PTE → sub-object metadata; new scheme: object metadata → sub-object-metadata). Also, the additional bits freed up for use by the sub-object aspect significantly increase the number of objects that can be represented by an immediate sub-object, reducing the frequency with which the sub-object cache needs to be cached.
[0293] Having a subobject metadata pointer per object reduces the complexity of having globals of different classes near each other: there is no need to share subobject metadata, so it doesn't matter how that metadata should be shared.
[0294] Placing sub-object metadata per object also reduces the dimensionality of the allocator's fragmentation. Whereas in the old implementation, co-located objects in the same allocator had to share their sub-object structures with each other, this new scheme allows objects with very different structures to share the same allocator; they do not need to be the same size.
[0295] Additionally, having subobject metadata per-object allows for greater flexibility in setting subobject metadata on objects. In the previous implementation, the internal class structure of the object needed to be known at allocation time, so an allocator with metadata matching the object's internal structure was selected. Now, after allocating the object, the setting of the type structure can be postponed until the first cast, from a void* type to another pointer type. While object structure can be risky to modify because it could potentially alter the structure of existing pointers from underneath, allowing owners to apply structure to objects in an up-to-date manner allows for greater flexibility in handling hard object allocation wrapping functions.
[0296] To allow for fast updates of subobject metadata pointers, the linker or sysruntime must create subobject metadata for each type that can be annotated to objects in memory before the program starts. Using this scheme, vanilla implementations of malloc can be extended to guard against hard objects rather than forcing them to use slabs or other types of allocators.
[0297] Additional benefits Refactoring metadata in this way has several additional benefits beyond those mentioned above. Function metadata no longer needs to be co-located with the start of the function, so functions no longer need to be dword aligned or a multiple of a dword in size, which may reduce memory fragmentation. · Because metadata is no longer annotated at page granularity, hard objects no longer impose the requirement that sections be aligned at page granularity. Because the global object ID can be chosen before the program starts and the exact size of the object ID is known (35-(pow2-object-size+3)), it is much easier to write code to convert pointers to global objects. This simplifies the process of deleting the refs-in-text table. · Static calls now set the CSR before executing the jal to declare the intention to make a call, and dynamic calls can be distinguished from dynamic jumps because the function offset is 0 when the call / jal / jalr instruction is executed, so you always know if a jump or call is being performed. Increasing the number of available metadata bits significantly increases the number of sub-objects that can be represented as immediate, increasing the number of bits available for sub-object IDs while reducing the complex distinction between upper and lower sub-object IDs.
[0298] Tool changes To support the above changes, some changes will be required in the tooling. The tool meaning of available sub-object mantissa bits should be extended from 3 to 8 in this embodiment. Removed distinction between top and bottom sub-object IDs. · Modify the hard object-assembly transformation to set the performing-tail-call CSR before the instruction that performs the tail call. In the post-linker, modify the jal or branch that performs the tail call to represent their offset in function ID space. If any of the features that can dynamically redistribute bits to and from subobject aspects are implemented, modify the tooling to set the subobject ID delta at link time. This requires a mid-linker between the merging and relocation stages of the linker.
[0299] Additionally, these changes will make your next adjustment easier. - Global object IDs are now predictable from the initial ELF content, so the refs-in-text table can be removed without using mid-linker xforms, only requiring post-linker xforms. In this implementation, the story about how virtual and physical addresses are managed is clearer, so the Spike proxy kernel is no longer dependent on managing memory mappings. Function / object size suffixes are highly tunable, allowing hard objects to be extended to work with different numbers of space bits and different register widths.
[0300] Optimization: Global Combination Many globals have little independent identity as objects. They are loaded at program start (or when a dynamic library is loaded) and are not individually deallocated (a dynamic library containing globals may be unloaded, which will deallocate all globals in the library at once). If globals are not transferred, their IDs do not change, they are not available to other threads, etc., they can be combined with other globals in the same section as large, composite objects. Access to individual globals can be mediated through subobject references. This reduces pressure on the object metadata cache; if all such globals fit into immediate subobjects, there is no additional burden on the subobject metadata cache. A hard-object-aware compiler can automatically identify such globals and group them into large, composite objects.
[0301] Flow Charts and Other Diagrams The present invention can be described through a series of methods deployed in hardware, but can also be understood through a series of flow charts. The drawings teach examples of various elements of the present invention that can be used alone or in combination.
[0302] Figure 1: A method for regulating the execution of programs on a computer. The computer has data addresses, and part of the data addresses is heap—global memory. The computer has a data-pointer, the data-pointer has a target-address and a durable-flag; Some data-pointers are annotated with ref-flag, The computer has a load instruction that can load data through a data address. The computer has a store instruction that can store data through a data address. The method includes: When a store instruction attempts to store a data-pointer to a data address, it performs the following steps: Check whether the data address is in heap-global memory. If "No", allow it as it is not relevant 0109, If yes, further check if the data-pointer's durable-flag is set to true. If yes, allow 0110, If no, clear the ref-flag annotated to the data-pointer to false when storing, or raise a fault 0104; When a load or store instruction attempts to load or store through the data-pointer 0105, perform the following steps: Checking whether the ref-flag of the data-pointer is set to true 0106, If yes, allow 0107, If no, raise a fault 0108.
[0303] Figure 2: A method for regulating the execution of programs on a computer. The computer has a data address, and part of the data address has data, Some data addresses are annotated with refable-version, The computer has a data-pointer, the data-pointer has a target-address and a time address; The computer has memory access instructions that access data through data-pointers. The method includes: When a memory access instruction attempts to access data through the data-pointer 0201, perform the following steps: Checking whether the time address of the data-pointer matches the refable-version annotated to the target-address of the data-pointer 0202; If yes, allow 0203, If no, raise a fault 0204.
[0304] Figure 3: A method for regulating the execution of programs on a computer, The computer has instruction addresses and data addresses, some of the instruction addresses contain instructions, and some of the data addresses contain data, The computer has a read instruction that reads data from a target data address; Part of the instruction address is annotated with mod-owner-id, Part of the data address is annotated with mod-ownable-id and may-read-suff-len, The method includes: When a read instruction at an instruction address attempts to read data from a target data address 0301, perform the following steps: Checking whether the mod-owner-id annotated to the instruction address matches the mod-ownable-id annotated to the target data address, except for the rightmost bit of the length of the may-read-suff-len bits annotated to the target data address 0302; If yes, allow 0303, If no, raise a fault 0304.
[0305] Figure 4: A method for regulating the execution of programs on a computer, The computer has instruction addresses and data addresses, some of the instruction addresses contain instructions, and some of the data addresses contain data, The computer has a write instruction that writes data to a target data address, Part of the instruction address is annotated with mod-owner-id, Part of the data address is annotated with mod-ownable-id and may-write-suff-len, The method includes: When a write instruction at an instruction address attempts to write data at a target data address 0401, perform the following steps: Checking whether the mod-owner-id annotated to the instruction address matches the mod-ownable-id annotated to the target data address, except for the rightmost bit of the length of the may-write-suff-len bits annotated to the target data address 0402; If yes, allow 0403, If no, raise a fault 0404.
[0306] Figure 5: A method for regulating the execution of programs on a computer, The computer has instruction addresses and data addresses, and some of the instruction addresses contain instructions. The computer has a make-reference instruction that makes a reference from a target data address, Part of the instruction address is annotated with mod-owner-id, Part of the data address is annotated with mod-ownable-id and may-make-ref-suff-len, The method includes: When a make-reference instruction at an instruction address attempts to make a reference from a target data address 0501, perform the following steps: Checking whether the mod-owner-id annotated to the instruction address matches the mod-ownable-id annotated to the target data address, except for the rightmost bit of the length of may-make-ref-suff-len bits annotated to the target data address 0502; If yes, allow 0503, If no, raise a fault 0504.
[0307] Figure 6: A method for regulating the execution of programs on a computer, The computer has an instruction address, and a part of the instruction address has an instruction; The computer has a call instruction that makes a call to a target instruction address; Part of the instruction address is annotated with mod-owner-id, A portion of the instruction address is annotated with may-call-suff-len, The method includes: When a call instruction at an instruction address attempts to make a call to a target instruction address 0601, performing the following steps: Checking whether the mod-owner-id annotated to the instruction address matches the mod-owner-id annotated to the target instruction address except for the rightmost bit of the length may-call-suff-len annotated to the target instruction address 0602; If yes, allow 0603, If no, raise a fault 0604.
[0308] Figure 7: A method for regulating the execution of programs on a computer, The computer has an instruction address, and a part of the instruction address has an instruction; The computer has a make-reference instruction that makes a reference from a target instruction address; Part of the instruction address is annotated with mod-owner-id, A part of the instruction address is annotated with may-make-ref-suff-len, The method includes: When a make-reference instruction at an instruction address attempts to make a reference from a target instruction address 0701, perform the following steps: Checking whether the mod-owner-id annotated to the instruction address matches the mod-owner-id annotated to the target instruction address except for the rightmost bit of the length may-make-ref-suff-len annotated to the target instruction address 0702; If yes, allow 0703, If no, raise a fault 0704.
[0309] 8a-8b: A method for regulating program execution on a computer, The computer has an instruction address, and a part of the instruction address has an instruction; The computer has data addresses, and part of the data addresses is heap—global memory. The computer has a function-pointer, the function-pointer has a target-address and a durable-flag, The function-pointer is annotated with a ref-flag, The computer has a store instruction that can store a function-pointer at a data address, The computer has a call instruction that makes a call through a function-pointer, The method includes: When a store instruction attempts to store a function-pointer to a data address, it performs the following steps: Check whether the data address is in heap-global memory 0802, If "No", allow it as it is not relevant 0810, If yes, further check if the function-pointer's durable-flag is set to true 0803, If yes, allow 0807, If no, clear the ref-flag annotated to the function-pointer to false when storing, or raise a fault 0804; When a call instruction attempts to make a call through a function-pointer 0805, perform the following steps: Checking whether the ref-flag of the function-pointer is set to true 0806, If yes, allow 0808, If no, raise a fault 0809.
[0310] Figure 9: A method for regulating the execution of programs on a computer, The computer has an instruction address, and a part of the instruction address has an instruction; Part of the instruction address is annotated with func-top-flag, The computer has a text-pointer, and the text-pointer has a target-address; The computer has a jump instruction that jumps through the text-pointer, The method includes: When a call or jump instruction attempts to jump through a function-pointer 0901, perform the following steps: Check whether the func-top-flag annotated to the target-address of the text-pointer is set to true 0902, If yes, allow 0903, If no, raise a fault 0904.
[0311] Figure 10: A method for regulating the execution of programs on a computer, The computer has an instruction address, and a part of the instruction address has an instruction; Part of the instruction address is annotated with mod-owner-id, The computer has a function-pointer, the function-pointer has a target-address and a cross-module-target-flag, The computer has a call instruction that makes a call through a function-pointer, The method includes: When a call instruction at an instruction address attempts to make a call through a function-pointer 1001, perform the following steps: Checking whether the mod-owner-id annotated to the instruction address matches the mod-owner-id annotated to the target-address of the function-pointer 1002; If yes, allow 1004, If no, further check whether the function-pointer's cross-module-target-flag is set to true 1003, If yes, allow 1004, If no, raise a fault 1005.
[0312] Figure 11: A method for regulating the execution of programs on a computer, The computer has an instruction address, and a part of the instruction address has an instruction; Part of the instruction address is annotated with refable-version, The computer has a function-pointer, the function-pointer has a target-address and a function-time, The computer has a call instruction that makes a call through a function-pointer, The method includes: When a call or jump instruction attempts to call or jump through a function-pointer 1101, perform the following steps: Checking whether the function-time of the function-pointer matches the refable-version annotated to the target-address of the function-pointer 1102; If yes, allow 1103, If no, raise a fault 1104.
[0313] Figure 12: A method for regulating the execution of programs on a computer, The computer has an instruction address, The computer has a current-function-start Some instruction addresses are annotated with text-page, and some text-pages have page-start-address and func-at-page-start, The computer has a return-pointer, and the return-pointer has a target-address and a function-top-offset; The computer has a return instruction that returns through a return-pointer, The method includes: When the return instruction returns through the return-pointer 1202, perform the following steps: Check whether the function-top-offset of the return-pointer is zero 1203, If yes, set current-function-start to func-at-page-start of text-page at target-address of return-pointer 1204 (and go to 1201 below), If no, set current-function-start to the page-start-address of the text-page annotating the target-address of the return-pointer plus the function-top-offset of the return-pointer 1205 (and go to 1201 below), Proceed from one of the branches mentioned above (1204 or 1205) and allow as done by procedure 1201.
[0314] Figure 13: A method for regulating the execution of programs on a computer, The computer has an instruction address, and a part of the instruction address has an instruction; The computer has a frame-pointer register, The computer has a return-pointer, which has a framepointer, The computer has a return instruction that returns through a return-pointer, The method includes: When the return command returns through the return-pointer 1302, perform the following steps: ·Setting the frame-pointer register to the value of the framepointer of the return-pointer 1303, · To be permitted as per procedure 1301.
[0315] FIG. 14: The method of FIG. 13, the computer further comprises a stack-limit register; The computer further encodes the return-pointer's framepointer as framepointer-up-relative-to-stack, in coordinates relative to the value of the stack-limit register, The method further includes: When the return command returns through the return-pointer, it replaces the following steps 1402: Set the frame-pointer register to the value of the return-pointer's framepointer-up-relative-to-stack plus the value of the stack-limit register 1403, · To be permitted as it is done by procedure 1401.
[0316] FIG. 15: The method of FIG. 9, text-pointer also has function-body-target-flag, The method further includes: When a jump instruction attempts to jump through a function-pointer 1501, · If the func-top-flag annotated to the target-address of the text-pointer is set to true 1502, If yes, allow 1504 If no, instead of raising a fault, take the following additional steps: Checking whether the function-pointer's function-body-target-flag is set to true 1503, If yes, allow 1504 If no, raise a fault 1505.
[0317] Figure 16: A method for regulating the execution of programs on a computer, comprising: The computer has an instruction address, and a part of the instruction address has an instruction; Part of the instruction address is annotated with refable-version, The computer has a return-pointer, the return-pointer has a target-address and a function-time, The computer has a return instruction that returns through a return-pointer, The method includes: When a return command attempts to return through the return-pointer 1601, perform the following steps: Check whether the function-time of the return-pointer matches the refable-version annotated to the target-address of the return-pointer 1602; If yes, allow 1603, If no, raise a fault 1604.
[0318] Figure 17: A method for regulating the execution of programs on a computer, comprising: The computer has an instruction address, and a part of the instruction address has an instruction; Part of the instruction address is annotated with func-top-flag, The computer has a jump instruction that transfers control to a target address, The method includes: When a jump instruction attempts to transfer control to a target-address 1701, it performs the following steps: · Checking whether the func-top-flag annotated to the target-address is set to true 1702; If yes, allow 1703 If no, raise a fault 1704.
[0319] FIG. 18: The method of FIG. 12, The computer further has a call instruction that transfers control to the function top of the target-address; The method further includes: When a call instruction transfers control to target-address 1802, ·Setting current-function-start to target-address 1803, · To be permitted as per procedure 1801.
[0320] FIG. 19: The method of FIG. 18, the program further comprises a function-start-to-function-metadata-map that annotates the function-start with function-metadata; The program further comprises current-function-metadata; The method further includes: · After a call or return instruction, set current-function-start to target-address, 1902; · setting the value of current-function-metadata to the function-metadata annotated to current-function-start by function-start-to-function-metadata-map 1903; · To be permitted as per procedure 1901.
[0321] FIG. 20: The method of FIG. 19, function-metadata also has mod-owner-id, the computer further has a data address, a portion of the data address further annotated with a mod-ownable-id; The computer further includes a memory access instruction that accesses data through the target pointer; The method further includes: When a memory access instruction at instruction address 2001 attempts to access data at target-data address 2001, perform the following steps: Checking whether the mod-owner-id in the current-function-metadata matches the mod-ownable-id annotated in the target-data address 2002, If yes, allow 2003, If no, raise a fault 2004.
[0322] FIG. 21: The method of FIG. 20, function-metadata further has mod-owner-suff-len, The method further includes: When a memory access instruction at instruction address attempts to access data at target-data address 2101, replacing the checking step with the following step: checking whether the mod-owner-id in the current-function-metadata matches the mod-ownable-id annotated to the target-data address except for the rightmost bit of the length mod-owner-suff-len in the current-function-metadata 2102; If yes, allow 2103 If no, raise a fault 2104.
[0323] FIG. 22: The method of FIG. 19, The function-metadata further has a danger-flag, The computer also has dangerous operations, The method further includes: · When a dangerous operation is performed 2201, Checking whether the danger-flag in current-function-metadata is set to true 2202, If yes, allow 2203 If no, raise a fault 2204.
[0324] FIG. 23: The method of FIG. 19, function-metadata further includes may-call-suff-len and mod-owner-id, the computer further has a call instruction to a target instruction address; The method further includes: When a call instruction attempts to make a call to a target instruction address 2301, Checking whether the mod-owner-id of the current-function-metadata matches the mod-owner-id of the function-metadata annotated to the target instruction address by the function-start-to-function-metadata-map, except for the rightmost bit of the length of may-call-suff-len of the function-metadata annotated to the target instruction address 2302; If yes, allow 2303 If no, raise a fault 2304.
[0325] FIG. 24: The method of FIG. 19, function-metadata further includes may-make-ref-suff-len and mod-owner-id, The computer further includes a make-ref instruction that can perform a lookup of a target instruction address; The method further includes: When a make-ref instruction attempts to reference a target instruction address 2401, Checking whether the mod-owner-id of the current-function-metadata matches the mod-owner-id of the function-metadata annotated to the target instruction address by the function-start-to-function-metadata-map, except for the rightmost bit of the length may-make-ref-suff-len of the function-metadata annotated to the target instruction address by the function-start-to-function-metadata-map 2402; If yes, allow 2403 If no, raise a fault 2404.
[0326] 25a-25b: The method of FIG. 19, The function-metadata further has a function-length, the computer further has a current-function-end; The method further includes: After a call or return instruction, set the value of current-function-metadata 2502, further setting the value of current-function-end to be current-function-start plus the function-length of the current-function-metadata 2504, · To be permitted by procedure 2501, When the instruction at the instruction address is attempted to be executed 2503, Checking whether the instruction address is both greater-than-or-equal-to the current-function-start and less-than the current-function-end 2505; If yes, allow 2506, If no, raise a fault 2507.
[0327] FIG. 26: The method of FIG. 19, function-metadata also has refable-version, The computer further has a function-pointer, the function-pointer having a target-address and a time-address; The computer further has a call instruction for making a call through the function-pointer; The method further includes: When a call instruction attempts to make a call through a function-pointer 2601, perform the following steps: Checking whether the time-address of the function-pointer matches the refable-version of the function-metadata annotated to the target-address of the function-pointer by the function-start-to-function-metadata-map 2602; If yes, allow 2603 If no, raise a fault 2604.
[0328] 27a-27c: A method for regulating program execution on a computer, The computer has an absolute-pointer, the absolute-pointer has a target-address, the target-address includes a data-page-index and a data-page-offset; An absolute-pointer has an object-id, The computer has a data address, the data address including a data-page-index and a data-page-offset; Part of the data address contains data, Part of the data-page-index-es is annotated with data-page meta-datum, Some data-page meta-datum-s have a page-class-id, Some page-class-id-s are annotated with page-class-meta-datum using page-class-id-map, Some page-class-meta-datum-s are annotated with map-obj-metadata-table, and map-obj-metadata-table annotates object-id with object-metadatum, Some object-metadatum-s have object-start and object-length, The computer has a memory-access instruction that accesses data at a target-address through an absolute-pointer, The method includes: When a memory-access instruction attempts to access data at a target-address through an absolute-pointer 2701, it performs the following steps: ·Find the data-page meta-datum annotated to the data-page-index of the target-address 2702, · Finding the page-class-id of a data-page meta-datum 2703, · Finding page-class-meta-datum annotated to page-class-id using page-class-id-map 2704, · Finding the map-obj-metadata-table annotated to page-class-meta-datum 2705, · Finding the object-metadata annotated to the object-id of an absolute-pointer by map-obj-metadata-table 2706, Finding the object-start and object-length of the object-metadatum 2707, · calculating object-end as the sum of object-start and object-length 2708; Check whether the target-address of an absolute-pointer is greater-than-or-equal-to the object-start 2709, If no, raise a fault 2712; If yes, check whether the target address of the absolute pointer is less than the object end. If no, raise a fault 2712; If yes, allow 2711.
[0329] 28a-28c: The method of FIGS. 27a-27c; absolute-pointer also has page-overflow-flag, The method further includes: When a memory-access instruction attempts to access data at target-address through an absolute-pointer 2801, Check whether absolute-pointer's page-overflow-flag is set to true 2802, If yes, calculate the effective data-page-index as the data-page-index of the target-address minus 1 (and proceed to 2805 below) 2803; If no, set the effective data-page-index to be the value of the data-page-index of the target-address (and proceed to 2805 below) 2804; If we proceed from one of the above branches (2803 or 2804) and find a data-page meta-datum, instead of finding the data-page meta-datum annotated to the effective data-page-index 2805, · Finding the page-class-id of a data-page meta-datum 2806, · Finding page-class-meta-datum annotated to page-class-id using page-class-id-map 2807, · Finding the map-obj-metadata-table annotated to page-class-meta-datum 2808, · map-obj-metadata-table to find the object-metadata annotated to the object-id of an absolute-pointer 2809, Finding object-start and object-length in object-metadatum 2810, · Calculating object-end as the sum of object-start and object-length 2811; Checking whether the target-address of an absolute-pointer is greater-than-or-equal-to the object-start 2812, If no, raise a fault 2815; If yes, check whether the absolute-pointer's target-address is less than the object-end. If no, raise a fault 2815; If yes, allow 2814.
[0330] 29a-29c: The method of FIGS. 27a-27c; an absolute-pointer further has an immediate-dist-to-start and an immediate-length, The method further includes: When the memory-access instruction attempts to access data at the target-address through the absolute-pointer 2901, further perform the following steps: ·Find the data-page meta-datum annotated to the data-page-index of the target-address 2902, · Finding the page-class-id of a data-page meta-datum 2903, · Finding page-class-meta-datum annotated to page-class-id using page-class-id-map 2904, · Finding the map-obj-metadata-table annotated to page-class-meta-datum 2905, · map-obj-metadata-table to find the object-metadata annotated to the object-id of the absolute-pointer 2906, Finding object-start and object-length in object-metadatum 2907, · Calculating immediate-subobject-start as the sum of object-start and absolute-pointer immediate-dist-to-start 2908; · Calculating the immediate-subobject-end as the sum of the immediate-subobject-start and the immediate-length of the absolute-pointer 2909; Checking whether the target-address of an absolute-pointer is greater-than-or-equal-to the immediate-subobject-start 2910, If no, raise a fault 2913; If yes, check whether the absolute-pointer's target-address is less than the immediate-subobject-end 2911. If no, raise a fault 2913; If yes, allow 2912.
[0331] FIG. 30: The method of FIGS. 27a-27c, an absolute-pointer further has immediate-dist-to-start, immediate-length, and immediate-granularity; The method further includes: When a memory-access instruction attempts to access data at target-address through absolute-pointer 3001, further perform the following steps: Find the data-page meta-datum annotated to the data-page-index of the target-address 3002, Finding the page-class-id of a data-page meta-datum 3003, · Finding page-class-meta-datum annotated to page-class-id using page-class-id-map 3004, · Finding the map-obj-metadata-table annotated to page-class-meta-datum 3005, · Finding the object-metadata annotated to the object-id of an absolute-pointer by map-obj-metadata-table 3006, Finding object-start and object-length in object-metadatum 3007, · computing the immediate-subobject-start as the sum of the object-start and the absolute-pointer's immediate-dist-to-start multiplied by the absolute-pointer's immediate-granularity 3008; · Calculating the immediate-subobject-end as the sum of the immediate-subobject-start and the immediate-length of the absolute-pointer multiplied by the immediate-granularity of the absolute-pointer 3009; Check whether the target-address of an absolute-pointer is greater-than-or-equal-to the immediate-subobject-start 3010, If no, raise a fault 3013; Check whether the target-address of an absolute-pointer is less than the immediate-subobject-end 3011. If no, raise a fault 3013; If yes, allow 3012.
[0332] 31a-31c: The method of FIGS. 27a-27c; The absolute-pointer further has a sub-object-id, Some page-class-meta-datum-s are further annotated in map-subobj-metadata-table, Some map-subobj-metadata-table-s further annotate the object-id with a subobj-metadata-table, Some subobj-metadata-table-s further annotate the sub-object-id with sub-object-metadatum-mem, Some sub-object-metadata-mem-s further have subobject-offset-from-object-start and subobject-length, The method further includes: When the memory-access instruction attempts to access data at the target-address through the absolute-pointer 3101, further perform the following steps: ·Find the data-page meta-datum annotated to the data-page-index of the target-address 3104; · Finding the page-class-id of a data-page meta-datum 3105, · Finding page-class-meta-datum annotated to page-class-id using page-class-id-map 3106, ·Finding the map-subobj-metadata-table annotated to page-class-meta-datum 3107, · map-subobj-metadata-table to find the subobj-metadata-table annotated with the object-id of absolute-pointer 3108, ·Finding the sub-object-metadata-mem annotated to the sub-object-id of the absolute-pointer by the subobj-metadata-table 3109, calculating the sub-object-start as the sum of the object-start of the object-metatum and the subobject-offset-from-object-start of the sub-object-metatum-mem 3110; · Calculating the sub-object-end as the sum of the sub-object-start and the sub-object-length of the sub-object-metadata-mem 3111; Checking whether the target-address of an absolute-pointer is greater-than-or-equal-to the sub-object-start 3112; If no, raise a fault 3102; If yes, check whether the target address of the absolute pointer is less than the sub-object end. If no, raise a fault 3102; If yes, allow 3103.
[0333] 32a-32c: The method of FIGS. 27a-27c; The absolute-pointer further has a sub-object-id, Some page-class-meta-datum-s are further annotated in map-subobj-metadata-table, Some map-subobj-metadata-table-s further annotate the object-id with a subobj-metadata-table, Some subobj-metadata-table-s further annotate the sub-object-id with sub-object-metadatum-mem, Some data-page meta-datum-s also have page-subobj-id-abs-base, Some sub-object-metadata-mem-s further have subobject-offset-from-object-start and subobject-length, The method further includes: When the memory-access instruction attempts to access data at the target-address through the absolute-pointer 3201, further perform the following steps: ·Find the data-page meta-datum annotated to the data-page-index of the target-address 3204, · Finding the page-class-id of a data-page meta-datum 3205, · Finding page-class-meta-datum annotated to page-class-id using page-class-id-map 3206, · Finding the map-subobj-metadata-table annotated to page-class-meta-datum 3207, · map-subobj-metadata-table to find the subobj-metadata-table annotated with the object-id of absolute-pointer 3208, · Calculating the absolute sub-object-id as the sum of the sub-object-id of the absolute-pointer and the page-subobj-id-abs-base annotated to the data-page meta-datum annotated to the data-page-index of the target-address 3209; ·Finding the sub-object-metadata-mem annotated to the absolute sub-object-id by the subobj-metadata-table 3210, Calculating the sub-object-start as the sum of the object-start of the object-metadatum and the subobject-offset-from-object-start of the sub-object-metadatum-mem 3211; · Calculating the sub-object-end as the sum of the sub-object-start and the sub-object-length of the sub-object-metadata-mem 3212; Check whether the target-address of the absolute-pointer is greater-than-or-equal-to the sub-object-start 3213, If no, raise a fault 3202; Check whether the target-address of the absolute-pointer is less than the sub-object-end 3214. If no, raise a fault 3202; If yes, allow 3203.
[0334] 33a-33c: The method of FIGS. 27a-27c; The absolute-pointer also has a subobj-topid, Some page-class-meta-datum-s are further annotated in map-subobj-metadata-table, Some map-subobj-metadata-table-s further annotate the object-id with a subobj-metadata-table, Some sub-object-metadata-tables also have a map-subobj-topid-to-absolute-subobj-id, Some map-subobj-topid-to-absolute-subobj-id further annotates absolute sub-object-id to subobj-topid, Some subobj-metadata-tables further annotate the absolute sub-object-id with a sub-object-metadata-mem, Some sub-object-metadata-mem-s further have subobject-offset-from-object-start and subobject-length, The method further includes: When the memory-access instruction attempts to access data at the target-address through the absolute-pointer 3301, further perform the following steps: ·Find the data-page meta-datum annotated to the data-page-index of the target-address 3304, · Finding the page-class-id of a data-page meta-datum 3305, · Finding page-class-meta-datum annotated to page-class-id using page-class-id-map 3306, ·Finding the map-subobj-metadata-table annotated to page-class-meta-datum 3307, · map-subobj-metadata-table to find the subobj-metadata-table annotated with the object-id of absolute-pointer 3308, ·From the subobj-topid of the absolute-pointer, find the absolute sub-object-id annotated by map-subobj-topid-to-absolute-subobj-id in the sub-object-metadata-table 3309, ·Finding the sub-object-metadata-mem annotated to the absolute sub-object-id by the subobj-metadata-table 3310; · Calculating the sub-object-start as the sum of the object-start of the object-metadatum and the subobject-offset-from-object-start of the sub-object-metadatum-mem 3311; · calculating the sub-object-end as the sum of the sub-object-start and the sub-object-length of the sub-object-metadatatum-mem 3312; Check whether the target-address of the absolute-pointer is greater-than-or-equal-to the sub-object-start 3313, If no, raise a fault 3302; Check whether the target-address of the absolute-pointer is less than the sub-object-end 3314. If no, raise a fault 3302; If yes, allow 3303.
[0335] Figure 34: A method for regulating the execution of programs on a computer, comprising: The computer has an instruction address, and a part of the instruction address has an instruction; The computer has a data address, and part of the data address has data, The computer has a memory-access instruction that accesses data at a target data address; Part of the instruction address is annotated with mod-owner-id, Part of the data address is annotated with mod-ownable-id and public-flag, The method includes: When a memory-access instruction at an instruction address attempts to access data at a target data address 3401, performing the following steps: Checking whether the mod-owner-id annotated to the instruction address is equal to the mod-ownable-id annotated to the target data address 3402; If yes, allow 3404, If no, check whether the public-flag annotated to the target data address is set to true 3403; If yes, allow 3404, If no, raise a fault 3405.
[0336] Figure 35: A method for regulating the execution of programs on a computer, comprising: The computer has a data address, and part of the data address has data, The computer has a write instruction that writes data to a target data address, Part of the data address is annotated with the writable-flag, The method includes: When a write instruction attempts to write data to a target data address 3501, Checking whether the writable-flag annotated to the target data address is set to true 3502; If yes, allow 3503, If no, raise a fault 3504.
[0337] 36a-36b: A method for regulating program execution on a computer, The computer has a data address, and part of the data address has data, the computer has a read instruction that reads data from a target data address and a write instruction that writes data to the target data address; Part of the data address is annotated with written-flag, The method includes: When a write instruction attempts to write data to a target data address 3602, · setting the written-flag annotated to the target data address to true 3603; · To be permitted by procedure 3601, When a read instruction attempts to read data from a target data address 3604, performing the following steps: Checking whether the written-flag annotated to the target data address is set to true 3605; If yes, allow 3606, If no, raise a fault 3607.
[0338] 37a-37b: A method for regulating program execution on a computer, The computer has a data address, and part of the data address has data, the computer has a read instruction that reads data from a target data address and a write instruction that writes data to the target data address; Part of the data address is annotated with written-flag, The computer has an unwritten indicator datum. The method includes: When a write instruction attempts to write data to a target data address 3702, · setting the written-flag annotated to the target data address to true 3703; · To be permitted by procedure 3701, When a read instruction attempts to read data from a target data address 3704, performing the following steps: Checking whether the written-flag annotated at the target data address is set to true 3705; If yes, allow 3707, If no, have the read instruction read the unwritten-indicator-datum instead of the data at the target data address 3706.
[0339] Figure 38: A method for regulating the execution of programs on a computer, comprising: The computer has an absolute-pointer, the absolute-pointer has a target-data-address and a public-target-flag; The computer has an instruction address, and a part of the instruction address has an instruction; The computer has a data address, and part of the data address has data, The computer has memory-access instructions that access data through absolute pointers, Part of the instruction address is annotated with mod-owner-id, Part of the data address is annotated with mod-ownable-id, The method includes: When a memory-access instruction at an instruction address attempts to access data through an absolute-pointer 3801, perform the following steps: Checking whether the mod-owner-id annotated to the instruction address is equal to the mod-ownable-id annotated to the target-data-address of the absolute-pointer 3802; If yes, allow 3804, If no, check whether the public-target-flag annotated to the absolute-pointer is set to true 3803; If yes, allow 3804, If no, raise a fault 3805.
[0340] Figure 39: A method for regulating the execution of programs on a computer, comprising: The computer has an absolute-pointer, the absolute-pointer has a target-data-address and a writable-target-flag, The computer has a data address, and part of the data address has data, The computer has a write instruction that writes data through an absolute pointer, The method includes: When a write command attempts to write data through an absolute pointer 3901, Check whether the writable-target-flag annotated to absolute-pointer is set to true 3902, If yes, allow 3903, If no, raise a fault 3904.
[0341] Figure 40: A method for regulating the execution of programs on a computer, comprising: The computer has data addresses, and part of the data addresses is stack memory. The computer has a data-pointer, Some data-pointers are annotated with ref-flag, The computer has memory-access instructions that allow it to access data through data-pointers, The method includes: When a memory-access command attempts to access data through a data-pointer 4002, perform the following steps: Checking whether the data-pointer points to a data address annotated as stack memory 4003, If "No", allow it as it is not relevant 4001, If yes, check if the data-pointer is annotated with ref-flag set to true 4004; If yes, allow 4005, If no, raise a fault 4006.
[0342] Figure 41: A method for regulating the execution of programs on a computer, comprising: The computer has instruction addresses and data addresses, some of the instruction addresses contain instructions, and some of the data addresses contain data, The computer has a stack, and some data addresses are annotated as part of the stack. The computer has a shadow-frame-pointer, The computer has a stack-object-pointer, which has a stack-pointer-target, a stack-pointer-start, and a size-in-bytes, The computer has memory access instructions that access data through a stack-object-pointer. The method includes: When a memory access instruction at instruction address 4102 attempts to access data through stack-object-pointer, perform the following steps: Check whether the stack-pointer-target of the stack-object-pointer is greater-than-or-equal-to the shadow-frame-pointer 4103, If "No", allow it as it is not relevant 4101, If yes, check whether the stack-pointer-target of the stack-object-pointer is less than the stack-pointer-start of the stack-object-pointer plus the size-in-bytes of the stack-object-pointer, and whether the stack-pointer-target of the stack-object-pointer is greater-than-or-equal-to the stack-pointer-start of the stack-object-pointer 4104, If yes, allow 4105, If no, raise a fault 4106.
[0343] FIG. 42: The method of FIG. 41, Some of the data addresses are further annotated as heap-global memory, the computer further has a store instruction that stores the value-being-stored at a target data address; The method further includes: When a store instruction attempts to store a value-being-stored at a target data address 4202, Check whether the value-being-stored is a stack-object-pointer and the target data address is annotated as heap-global memory 4203, If yes, raise a fault 4204; If no, allow as not relevant 4201.
[0344] 43a-43b: The method of FIG. 41, the computer further comprises a call instruction; the computer further comprises a stack-pointer; the computer further has a stack-object-floor; The computer further includes a narrow-pointer operation that creates a new stack-object-pointer, the narrow-pointer operation having a target-pointer parameter and a new-object-size parameter; The method further includes: When a narrow-pointer operation is called with the target-pointer parameter value and the new-object-size parameter value 4302, · setting the new stack-object-floor to the minimum of the current stack-object-floor value and the target-pointer parameter value of the narrow-pointer operation 4303; · To be permitted by procedure 4301, When the call instruction is executed 4304, Checking whether stack-pointer is less-than-or-equal-to stack-object-floor 4305, If yes, allow 4306, If no, raise a fault 4307.
[0345] FIG. 44: The method of FIGS. 43a-43b, The computer further has a shadow-frame-pointer; the computer further has a return instruction; The method further includes: · When the return instruction is executed 4402, · setting stack-object-floor to the value that shadow-frame-pointer has at the start of the return instruction 4403; · To be permitted as it is done by procedure 4401.
[0346] Figure 45: A method for regulating the execution of programs on a computer, comprising: The computer has a stack, some data addresses are annotated as part of the stack, and the data addresses annotated as part of the stack have a newer-than-or-equal-to total order, The computer has a shadow-frame-pointer, The computer has a stack-object-pointer, and the stack-object-pointer has a stack-object-pointer-frame-pointer, The computer has a return register, the return register has a ref flag, The computer has a return instruction, The method includes: When a return instruction is executed, if the value held in the return-register is a stack-object-pointer 4501, perform the following steps: Check whether the stack-object-pointer-frame-pointer of the stack-object-pointer is newer-than-or-equal-to the shadow-frame-pointer 4502, If no, allow 4504; If yes, raise a fault or clear the ref-flag in the return-register 4503.
[0347] Figure 46: A method for regulating the execution of programs on a computer, comprising: The computer has a stack, some data addresses are annotated as part of the stack, and the data addresses annotated as part of the stack have a newer-than-or-equal-to total order, The computer has a shadow-frame-pointer, The computer has a stack-limit register, The computer has a stack-object-pointer, and the stack-object-pointer has a framepointer-up-relative-to-stack, The computer has a return register, The computer has a return instruction, The method includes: When a return instruction is executed, if the value held in the return-register is a stack-object-pointer 4601, perform the following steps: · Calculating stack-object-pointer-frame-pointer as the sum of the value held in the stack-limit register and the framepointer-up-relative-to-stack of the stack-object-pointer held in the return-register 4602; Check whether stack-object-pointer-frame-pointer is newer-than-or-equal-to shadow-frame-pointer 4603, If no, allow 4604; If yes, raise a fault 4605.
[0348] Figure 47: A method for regulating the execution of programs on a computer, comprising: The computer has data addresses, and some of the data addresses have a newer-than-or-equal-to total order. The computer has a shadow-frame-pointer, The computer has a stack-object-pointer, which has a stack-pointer-target and a stack-object-pointer-frame-pointer, The computer has a store instruction that stores a value-being-stored at a target data address; The method includes: When a store instruction attempts to store a value-being-stored to a target data address, if the target data address is a stack-object-pointer and the value-being-stored is a stack-object-pointer 4702, perform the following steps: Check whether the target data address stack-pointer-target is newer-than-or-equal-to the shadow-frame-pointer 4703, · Allowed because it is not related 4701, If no, check whether the stack-object-pointer-frame-pointer of the target data address is newer-than-or-equal-to the stack-object-pointer-frame-pointer of the value-being-stored 4704; If yes, allow 4705, If no, raise a fault 4706.
[0349] Figure 48: A method for regulating the execution of programs on a computer, comprising: The computer has data addresses, and some of the data addresses have a newer-than-or-equal-to total order. The computer has a shadow-frame-pointer, The computer has a stack-limit register, The computer has a stack-object-pointer, which has a stack-pointer-target and a framepointer-up-relative-to-stack, The computer has a store instruction that stores a value-being-stored at a target data address; The method includes: When a store instruction attempts to store a value-being-stored to a target data address, if the target data address is a stack-object-pointer and the value-being-stored is a stack-object-pointer 4802, perform the following steps: Check whether the target data address stack-pointer-target is newer-than-or-equal-to the shadow-frame-pointer 4803, If yes, allow it as it is not relevant 4801, If no, proceed as follows: Calculating the stack-object-pointer-frame-pointer of the target data address as the sum of the stack-limit register and the framepointer-up-relative-to-stack of the target data address stack-object-pointer, and 4804, · Calculating the value-being-stored stack-object-pointer-frame-pointer as the sum of the stack-limit register and the value-being-stored stack-object-pointer's framepointer-up-relative-to-stack 4805; Checking whether the stack-object-pointer-frame-pointer of the target data address is newer-than-or-equal-to the stack-object-pointer-frame-pointer of the value-being-stored 4806, · Allow 4807, If no, raise a fault 4808.
[0350] Figures 49a-49b: A method for regulating program execution on a computer, The computer has a data address, and part of the data address has data, the computer has at least one register annotated as a callee-save register; the computer has a where-saved array, the where-saved array having sub-registers corresponding to each of at least one register annotated as a callee-save register; the computer has an unsaved-marker-value that can be stored in the sub-registers of the where-saved array, the unsaved-marker-value not corresponding to any target data address; The computer has a store instruction that stores data from a store-source-register to a target data address; The method includes: When a store instruction stores data from a store-source-register to a target data address 4905, Checking whether a store-source-register was annotated as a callee-save register 4906, If "No", allow it as it is not relevant 4901, If yes, check whether the sub-register of the where-saved array corresponding to the store-source-register has the unsaved-marker-value as its value 4907; If "No", allow it as it is not relevant 4902, If yes, set the target data address to the sub-register of the where-saved array corresponding to the store-source-register 4908; · To be permitted by procedure 4904, When an operation other than a store instruction that reads the target register is executed 4909, Checking whether the target register was annotated as a callee-save register 4910; If "No", allow it as it is not relevant 4903, If yes, check whether the sub-register of the where-saved array corresponding to the target register has the unsaved-marker-value as its value 4911; If no, allow 4912; If yes, raise a fault 4913.
[0351] FIG. 50: The method of FIGS. 49a-49b, the computer further includes a load instruction that loads data from a target data address into a load-destination-register; The method further includes: When a load instruction loads data from a target data address into a load-destination-register 5004, Checking whether the load-destination-register was annotated as a callee-save register 5005, If "No", allow it as it is not relevant 5001, If yes, checking whether the sub-register of the where-saved array corresponding to the load-destination-register has the target data address as its value 5006; If "No", allow it as it is not relevant 5002, If yes, record the unsaved-marker-value in a sub-register of the where-saved array corresponding to the load-destination-register 5007; · To be permitted by procedure 5003.
[0352] FIG. 51: The method of FIG. 50, wherein the method further includes: When a load instruction loads data from a target data address into a load-destination-register 5103, Checking whether any sub-register of the where-saved array has the target data address as its value 5104; If "No", allow it as it is not relevant 5101, If yes, check whether the sub-register of the where-saved array corresponds to a register other than the load-destination-register of the load instruction 5105; If yes, raise a fault 5106; · If "No", allow as not relevant 5102.
[0353] FIG. 52: The method of FIG. 50, wherein the method further includes: When a store instruction stores data at a target data address 5202, Checking whether any sub-register of the where-saved array has the target data address as its value 5203; If yes, raise a fault 5204; · If "No", allow as not relevant 5201.
[0354] Figure 53: The method of Figure 50, the computer further has a return instruction; The method further includes: · When a return instruction is executed 5302, · Checking whether all sub-registers of the where-saved array have the unsaved-marker-value as their value 5303; If yes, allow it as it is not relevant 5301, If no, raise a fault 5304.
[0355] FIG. 54: The method of FIG. 41, stack-object-pointer further has writable-target-flag, The computer further has a store instruction that stores data through the stack-object-pointer; The method further includes: When a store instruction attempts to store data through a stack-object-pointer 5402, Checking whether stack-object-pointer has a clear writable-target-flag 5403, If yes, raise a fault 5404; · If "No", allow as not relevant 5401.
[0356] Figures 55a-55b: The method of Figures 49a-49b; the computer further has a may-restore-flag; The computer further includes a restore-callee-save-reg-state operator; The computer further includes an operation for writing the target register; The method further includes: When the operation performs an operation that writes a callee-save target register and the sub-register of the where-saved array that corresponds to the callee-save target register has an unsaved-marker-value 5502, clearing may-restore-flag to false 5503, · To be permitted by procedure 5501, ·When the restore-callee-save-reg-state operator is executed 5504, · Checking whether may-restore-flag is true 5505, If yes, allow 5506, If no, raise a fault 5507.
[0357] Figures 56a-56e: The method of Figures 49a-49b; The computer further includes an operation for writing the target register; the computer further has a for-this-func-flag; The computer further has a call instruction and a return instruction; the computer further has a save-callee-save-reg-state operator and a restore-callee-save-reg-state operator; The method further includes: When the call instruction is executed 5605, ·Checking whether for-this-func-flag is true 5606, If no, raise a fault 5623; If yes, clear for-this-func-flag to false 5607, · To be permitted by procedure 5602, ·When the save-callee-save-reg-state operator is executed 5608, · setting for-this-func-flag to true 5609, · To be permitted by procedure 5603, 5610, when an operation that accesses a target register is executed; If the target register is annotated as a callee-save register, If "No", allow it as it is not relevant 5601, If yes, check if for-this-func-flag is true 5612, If yes, allow 5613, If no, raise a fault 5614; ·When the restore-callee-save-reg-state operator is executed 5615, ·Checking that for-this-func-flag is true 5616, If yes, allow 5618, If no, raise a fault 5617; · When the return instruction is executed 5619, ·Checking whether for-this-func-flag is false 5620, If no, raise a fault 5622; · If yes, set for-this-func-flag to true 5621, · To be permitted as performed by procedure 5604.
[0358] 57a-57c: A method for regulating program execution on a computer, the computer has at least one integer-register, each of the at least one integer-register-s having a unique register-id; The computer has an array of int-register-written-flag-s, each corresponding to at least one integer-register; the computer has an array of ok-to-return-flag-s, one for each of at least one integer-register; the computer has a set-ok-to-return-flag operator, the set-ok-to-return-flag operator has a target register-id parameter; The computer has at least one operator that reads a value of a target register, the target register having a target register id; The computer has a return instruction, The method includes: When an operator that reads the value of a target register is executed and the target register has the target register id 5704, ·Check whether the int-register-written-flag corresponding to the target register id in the array of int-register-written-flag-s is false 5705, If yes, raise a fault 5710; If "No", allow it as it is not relevant 5701, · when the set-ok-to-return-flag operator is called with the target register-id parameter value 5706, · setting an ok-to-return-flag in the array of ok-to-return-flag-s corresponding to the integer register having the target register-id parameter value 5707; · To be permitted by procedure 5702, · When the return instruction is executed 5708, Clearing to false any int-register-written-flag-s corresponding to any register-id corresponding to a false ok-to-return-flag 5709, · To be permitted as performed by procedure 5703.
[0359] 58a-58c: A method for regulating program execution on a computer, the computer has at least one integer-register, each of the at least one integer-register having a unique register-id; The computer has an array of int-register-written-flag-s, each corresponding to at least one integer-register; the computer has an array of ok-to-return-flag-s, one for each of at least one integer-register; the computer has a set-ok-to-return-flag operator, the set-ok-to-return-flag operator has a target register-id parameter; The computer has at least one operator that reads a value of a target register, the target register having a target register id; The computer has an unwritten indicator datum. The computer has a return instruction, The method includes: When an operator that reads the value of a target register is executed and the target register has the target register id 5805, ·Check whether the int-register-written-flag corresponding to the target register id in the array of int-register-written-flag-s is false 5806; If "No", allow it as it is not relevant 5801, If yes, read the unwritten-indicator-datum value instead of the value in the target register 5807; · To be permitted by procedure 5802, · when the set-ok-to-return-flag operator is called with the target register-id parameter value 5808, · setting an ok-to-return-flag in the array of ok-to-return-flag-s corresponding to the integer register having the target register-id parameter value 5809; · To be permitted by procedure 5803, · When the return instruction is executed 5810, Clearing to false any int-register-written-flag-s corresponding to any register-id corresponding to a false ok-to-return-flag 5811, · To be permitted by procedure 5804.
[0360] 59a-59c: A method for regulating program execution on a computer, the computer has at least one integer-register, each of the at least one integer-register having a unique register-id; The computer has an array of int-register-written-flag-s, each corresponding to at least one integer-register; the computer has an array of ok-to-call-flag-s, one for each of at least one integer-register; the computer has a set-ok-to-call...
Claims
1. 1. A method for regulating program execution by a microprocessor, the microprocessor organizing memory into pages, the microprocessor having at least one pointer, the at least one pointer including a page index, the microprocessor providing memory-access operations to target data through target pointers, the memory-access operations using the page index of the target pointer to locate a memory page containing the target data, the method comprising: providing an object-id for at least one of said pointers; providing a metadata map from the at least one pointer to at least one object meta-datum; and implementing a metadata map using a data structure that maps from said page index and object-id of said at least one pointer to said at least one object metadata; A method comprising:
2. 2. The method of claim 1, further comprising providing a page-overflow-flag for at least one of the pointers, and implementing the metadata map to retrieve from a different page index when the page-overflow-flag is set.
3. 3. The method of claim 2, further comprising: when retrieving from said different page indexes, doing so by subtracting one from said page index.
4. 2. The method of claim 1, further comprising: implementing a metadata map with a first mapping from the page index of the pointer to a map object metadata table; and a second mapping from the object-id of the pointer to the at least one object metadata using the map object metadata table.
5. 5. The method of claim 4, further comprising providing a page-overflow-flag for at least one of the pointers, and implementing the metadata map to retrieve from a different page index when the page-overflow-flag is set.
6. 6. The method of claim 5, further comprising: when retrieving from said different page indexes, doing so by subtracting one from said page index.
7. 5. The method of claim 4, further comprising implementing a first mapping by placing a page-class-id in a page table entry corresponding to said page index and pointing said page-class-id to said map object metadata table.
8. 8. The method of claim 7, further comprising providing a page-overflow-flag for at least one of the pointers, and implementing the metadata map to retrieve from a different page index when the page-overflow-flag is set.
9. 9. The method of claim 8, further comprising: when retrieving from the different page indexes, doing so by subtracting one from the page index.
Citation Information
Patent Citations
High-reliability memory address designation method
JP1995006095A
System for making single object pointer by uniting local object address with global object identifier
JP1996077023A
Object oriented communication equipment
JP2000322260A
Thread execution device
JP2003296128A
Apparatus and method for controlling the use of bounded pointers
JP2019505052A