Patent Description:
Typically, where there is a trust boundary between two software components a cross-domain communication mechanism is used to communicate between the two software components and the cross-domain communication mechanism is expensive in terms of compute cycles and/or introduces latency. In an example, in the case where the trust boundary involves a transition from a trusted execution environment state to an insecure state while deploying side channel mitigations, the cross-domain communication mechanism introduce around <NUM>,<NUM> compute cycles. In another example where the trust boundary is across two virtual machines the cross-domain communication mechanism involves use of a scheduler which introduces latency of around <NUM> milliseconds.

The embodiments described below are not limited to implementations which solve any or all of the disadvantages of known methods for memory deallocation across a trust boundary. <CIT> describes a shared memory region, a plurality of compute nodes, and a plurality of processes executable in the plurality of compute nodes. A first process of the plurality of processes is to use an allocation interface to perform locality-aware allocation of a first zone of the shared memory region, the first zone owned by the first process and accessible by the first process and a second process of the plurality of processes. The locality-aware allocation of the first zone to allocate the first zone that has a specified proximity to a given compute node of the plurality of compute nodes.

The following presents a simplified summary of the disclosure in order to provide a basic understanding to the reader. This summary is not intended to identify key features or essential features of the claimed subject matter nor is it intended to be used to limit the scope of the claimed subject matter. Its sole purpose is to present a selection of concepts disclosed herein in a simplified form as a prelude to the more detailed description that is presented later.

A method of memory deallocation across a trust boundary between a first software component and a second software component is described. Some memory is shared between the first and second software components. An in-memory message passing facility is implemented using the shared memory. The first software component is used to deallocate memory from the shared memory which has been allocated by the second software component. The deallocation is done by: taking at least one allocation to be freed from the message passing facility; and freeing the at least one allocation using a local deallocation mechanism while validating that memory access to memory owned by data structures related to memory allocation within the shared memory are within the shared memory.

The detailed description provided below in connection with the appended drawings is intended as a description of the present examples and is not intended to represent the only forms in which the present example are constructed or utilized. The description sets forth the functions of the example and the sequence of operations for constructing and operating the example. However, the same or equivalent functions and sequences may be accomplished by different examples.

Typically, where there is a trust boundary between two software components a cross-domain communication mechanism is used to communicate between the two software components and the cross-domain communication mechanism is expensive in terms of compute cycles and/or introduces latency. The cross-domain communication mechanism also imposes synchronization. As a result it is problematic to deallocate memory across a trust boundary, that is, to use a first software component to deallocate memory that a second software component has allocated, where there is a trust boundary between the two software components. Since synchronization is imposed it is difficult for the two software components to run concurrently without needing to synchronize for memory allocation events.

Trust boundaries occur between software components in many types of computing device such as those illustrated in <FIG> which are end user devices such as smart phone <NUM>, laptop computer <NUM>, smart watch <NUM>, tablet computer <NUM>, head worn augmented reality computing device <NUM>; and also enterprise computing devices and cloud computing devices such as compute nodes <NUM> in a data center. Note that the computing devices illustrated in <FIG> are examples and are not intended to limit the scope of the technology.

Each of the computing devices of <FIG> comprises various components which are described in detail with reference to <FIG>. <FIG> shows some but not all of these components of a single computing device for clarity. <FIG> shows that each of the computing devices has memory <NUM> storing software components <NUM> which share a shared memory <NUM> and which are separated by a trust boundary. The shared memory <NUM> stores a message passing facility <NUM>. Each computing device also has one or more processors <NUM> and one or more interfaces <NUM> such as a communications interface to enable the computing device to communicate with external memory, other computing devices, communications networks and so on.

The technology of the present disclosure has at least two software components <NUM> which are separated by a trust boundary and non-limiting examples of these software components are given with reference to <FIG> below. Each software component executes one or more threads. A thread is a serial execution of instructions on one side of the trust boundary and which does not flow across the trust boundary.

The software components <NUM> share some memory shown at <NUM> in <FIG>. Each software component <NUM> is able to allocate and deallocate blocks of memory in the shared memory <NUM>. Each software component <NUM> has at least one allocator for the shared memory. If a software component has memory outside the shared memory then the software component has at least one allocator for the memory outside the shared memory. An allocator is a software component that is responsible for partitioning memory into objects that can be used by other components and reusing them once they have been returned.

The shared memory <NUM> is used to implement a message passing facility <NUM>. The message passing facility enables messages to be sent between threads either side of a trust boundary without using a conventional cross-domain communication mechanism. It uses in-memory operations so that the message passing facility is very efficient as compared with using a conventional cross-domain communication mechanism. By using the message passing facility as described herein it is possible to efficiently allocate and deallocate memory across a trust boundary in a secure manner. The technology is usefully deployed in a wide variety of scenarios, some of which are described with reference to <FIG>.

The software components <NUM> are separated by a trust boundary. Some of the memory visible to the software components <NUM> is shared (shared memory <NUM> which is a region of memory <NUM>) between two or more software components. There is a message-passing facility in the shared memory <NUM> region. Allocations are performed within the shared memory <NUM> by any of the software components <NUM> that share the shared memory <NUM>. Allocations within the shared memory <NUM> are deallocated by any of the software components <NUM> that share the shared memory <NUM>, irrespective of which software component freed it (by using the message-passing facility <NUM> to pass it back to the owning software component).

Deallocation comprises at least two operations which are: marking a piece of memory as no longer in use; and making a piece of memory available for reuse. In a typical C-style programming environment, these two operations are typically conflated (a call to free() usually both marks an object as unused and immediately makes it available for reuse). The technology of the present disclosure splits those into two steps, where the first either triggers the second immediately for local allocations or adds it to a message queue for remote allocations so that a remote allocator will make it available for reuse later.

<FIG> shows the situation where the first software component is in an enclave <NUM> and the second software component is a program <NUM> outside the enclave. The enclave <NUM> has access to some private memory which is represented by the rectangle depicting the enclave <NUM> in <FIG>. The program outside the enclave <NUM> has access to some but not all of the memory. That is, the enclave <NUM> and the program outside the enclave share some memory. In an example, threads executing inside the enclave are trusted whereas threads executing in the program outside the enclave are not trusted. In an example, the technology of the present disclosure enables a thread inside the enclave (referred to herein as a green thread G for clarity) to allocate some memory within the shared memory by using an allocator. In an example, the technology of the present disclosure enables a thread inside the enclave (a G thread) to deallocate some memory within the shared memory which has been allocated by a thread in the program outside the enclave (an R thread). The technology is symmetric. So a thread in the program outside the enclave (R thread) is able to deallocate some memory in the enclave which was allocated by the G thread.

In an enclave system such as that shown in <FIG>, the code running inside an enclave can see and modify the memory outside and the program <NUM> outside is responsible for all untrusted communication (for example, fetching encrypted data from a disk or over a network). It is possible for the program <NUM> outside the enclave to pre-allocate memory buffers and rings that the enclave <NUM> can use to communicate, but it is often more convenient for the enclave <NUM> to be able to allocate arbitrary memory objects.

The technology of the present disclosure enables the enclave <NUM> to cheaply allocate memory outside of the enclave <NUM> (where cheaply means with fewer computing resources). Thus it is also comparatively easy for the allocator to dynamically adjust the amount of memory used by the enclave <NUM> for communication, rather than pre-allocating everything. For this to be efficient, the untrusted code running outside of the enclave <NUM> is able to free the memory with low overhead. The technology of the present disclosure enables the program <NUM> outside of the enclave to free the memory with low overhead.

<FIG> is a schematic diagram of a more secure virtual machine <NUM> and a less secure virtual machine <NUM>. The more secure virtual machine has memory depicted by the larger rectangle and the less secure virtual machine shares some but not all of that memory as indicated in <FIG>. This type of arrangement is found in some well-known operating systems where the kernel is split into two parts that are protected by the hypervisor. A less trusted virtual machine runs the main operating system and normal applications. The more secure virtual machine runs integrity and security services and is able to see memory owned by the less trusted virtual machine. The technology of the present disclosure removes some of the constraints on code running in the more secure virtual machine and makes it possible for the secure code to create complex data structures for the less secure code to consume.

<FIG> is a schematic diagram of a high privilege library compartment <NUM> and a lower privilege library compartment <NUM>. The high privilege library compartment <NUM> has memory depicted by the larger rectangle and the less privileged library compartment <NUM> shares some but not all of that memory as indicated in <FIG>.

In a software compartmentalization arrangement, some part of a program runs with lower privilege. It is common to wish to move an existing library into a lower privilege to minimize attack surface. For example, image and video decoding libraries are typically written in unsafe languages and handle untrusted data so are a common vector for exploits. Running such a library with a very limited set of privileges makes this significantly safer.

There are several mechanisms that make it easy to enforce strong isolation, for example separate processes with shared memory, software-based fault isolation (SFI) sandboxes such as those used by WebAssembly (trade mark) implementations, and hardware isolation features. These impose a performance penalty to transition between the high privilege library compartment and the low privilege library compartment. Each technique can be used to make the whole of the untrusted component's memory visible to the trusted component.

This is sufficient as long as the library interface is implemented in terms of functions that take and return only simple (primitive) types. Unfortunately, this is rarely the case and most libraries expect users to construct or consume complex data structures. To improve the programming model for software compartmentalization, the technology of the present disclosure gives a lightweight ability for the high privilege component to be able to allocate memory inside the low privilege library and for both the low privilege library and high privilege library to be able to free this memory.

<FIG> is a diagram of the general case, having a first software component <NUM> and a second software component <NUM> divided by a trust boundary (where the trust boundary is not illustrated in <FIG>). The first software component <NUM> has memory depicted by the larger rectangle and the second software component <NUM> shares some but not all of that memory as indicated in <FIG>. Using the message passing facility, which is in the shared memory, the technology of the present disclosure enables efficient and secure deallocation of memory by a thread, where the memory has been allocated by a different thread on the other side of the trust boundary.

<FIG> is a flow diagram of a method of memory allocation performed by a thread G which is on one side of a trust boundary in order to allocate memory on the other side of the trust boundary. <FIG> is described for the case where the thread G is on a more trusted side of the trust boundary. However, note that the method is symmetric and also works where the thread performing the method is on a less trusted side of the trust boundary. The method of <FIG> also operates for situations involving mutual distrust, where the thread performing the method is in a first software component and on the other side of the trust boundary is a second software component, where the first and second software components mutually distrust one another.

Thread G executes <NUM> on one side of the trust boundary, such as a more trusted side of the trust boundary. Thread G reaches a point in its execution where there is a request <NUM> to create an object in shared memory where the shared memory is shared with the other side of the trust boundary. Thread G checks <NUM> whether it already has a local allocator. A local allocator is an allocator which has its own arena (a data structure comprising virtual memory space which maps to physical memory) and metadata recording which blocks are currently allocated in the virtual memory space). In some examples, the virtual memory space of a local allocator maps to physical memory owned by the other side of the trust boundary. Note that in systems without virtual memory, such as embedded devices where there are overlapping memory protection unit (MPU)-protected regions for components, then a local allocator is a region of physical memory owned by the other side of the trust boundary. In a preferred example, most of the state of a local allocator is in private memory, not shared memory.

If there is no local allocator for thread G then thread G reserves <NUM> some space in the shared memory for a message queue to be used by the software on the other side of the trust boundary. In some examples the reservation is done using a shared pointer to a start of a region in the shared memory and an atomic operation. In other examples the reservation is done using a cross-trust boundary call which is expensive in terms of compute cycles and/or time but is not performed very often. The software on the other side of the trust boundary receives the cross trust boundary call, reserves some space in the shared memory and informs thread G.

If there is a local allocator for thread G, but the local allocator is too small to satisfy the allocation, then a cross-trust boundary synchronization operation is done to reserve some memory in the shared region.

Thread G instructs <NUM> the local allocator to directly construct the object in the shared memory. Because the local allocator already has an arena it is able to directly construct the object in the region of shared memory that it has reserved. The reservation step guarantees that no other allocator will try to allocate objects in that reserved region.

At check point <NUM>, if the thread G already has a local allocator then the process moves to operation <NUM>.

During operation <NUM> when the object is being constructed, thread G makes allocator state checks <NUM>. That is, thread G checks <NUM> whether the local allocator harmed memory on the same side of the trust boundary as thread G. The check <NUM> comprises checking that every address used based on data in the shared memory region is, itself, in the shared memory region. The check is done by ensuring that any memory accesses that occur as part of the process of allocation and which depend on untrusted data are within the shared memory region. If the check finds that memory outside of the shared region would be read of written as a result of corrupted or malicious data in the shared region then an error recovery path is triggered <NUM>. If the check <NUM> finds no problem , the process returns to operation <NUM> and thread G continues to execute.

If the local allocator harms memory on the same side of the trust boundary as the local allocator there is no problem since the software on the same side of the trust boundary as the local allocator is untrusted and it is expected that it may corrupt memory on its side of the trust boundary.

The method of <FIG> describes memory allocation performed by a thread to allocate memory on the other side of the trust boundary. The case where a thread allocates memory on the same side of the trust boundary as the thread is conventional and so is not described here.

In an example, every thread has an allocator for local allocation, but there is a single allocator for a first software component to allocate memory owned by the second software component. The single allocator of the first software component is protected by a lock and is accessed by multiple threads.

In another example, each thread of the first software component has a local allocator and a remote allocator (so two instances of a memory allocator).

<FIG> is a flow diagram of a method performed by a thread R in the other side of the trust boundary to thread G. In an example, thread R is part of untrusted code but this is an example only as thread R can be any thread on the other side of the trust boundary from thread G.

Thread R executes <NUM> and reaches <NUM> a point in its execution where there is a request to deallocate memory region M from shared memory. Memory region M was allocated by thread G. The thread R identifies <NUM> which allocator allocated M. In this example, thread R finds that the local allocator of thread G allocated M. The identification is done by looking up in a data structure (referred to herein as an allocator index). In some examples the allocator index is maintained by the first software component in a shared memory region to which the second software component has access. In some examples, the first software component has a copy of the allocator index and the second software component has a copy of the allocator index and these copies are synchronized during operation <NUM> of <FIG>.

Thread R adds a request to free M to a record in the in-memory message passing facility. The request is made immediately or is put into a batch and made as part of a batch of requests. More detail about batching and situations when batching of request is not appropriate is given below.

The record is associated with the allocator which allocated M. In an example, the record is a queue and there is one queue for each allocator. The request to free M is added to the queue of the allocator which allocated M. The thread R then proceeds to execute <NUM> and the process repeats.

<FIG> is a flow diagram of a method performed by the thread which originally allocated M. In the example of <FIG>, the thread which originally allocated M is thread G. Thread G is in the other side of the trust boundary to thread R. Thread G is executing <NUM>. It looks in the record in the in-memory message passing facility to find which requests to free memory regions are waiting for it. It finds a request to deallocate M which is waiting for thread G since thread G previously allocated M. Thread G takes the request and instructs <NUM> the local allocator of thread G to directly deallocate M. Since the local allocator carries out the deallocation directly there is no cross-trust boundary call and the deallocation is efficient.

Operation <NUM> is carried out while thread G validates <NUM> by making a range check that the memory accesses performed during operation <NUM> are within the shared range. If the shared memory region is not contiguous there are a plurality of ranges to check everything points inside the shared memory.

If the validation fails then an alert is triggered <NUM> and/or memory is cleared. If the validation is successful then the process returns to operation <NUM> where thread G executes.

In the mutual distrust case, where the first and second software components distrust one another, the method of <FIG> is symmetric as is the method of <FIG>. That is, where thread G performs the method of <FIG> then thread R performs the method of <FIG>. If there is a hierarchical trust relationship then the untrusted component does not need to perform validation (it trusts the other component) and it is free to do a lot more caching of messages.

The technology of the present disclosure uses a message passing facility that is implemented in the shared memory. In an example the message passing facility is implement using only the shared memory.

An example of the message passing facility is now given with reference to <FIG> and note that this is one example only and others are possible.

<FIG> shows a first software component <NUM> and a second software component <NUM> as in <FIG> which are separated by a trust boundary. The first software component <NUM> is in memory schematically illustrated as rectangle <NUM> and the second software component shares some of that memory as illustrated by rectangle <NUM> which is within rectangle <NUM>.

The second software component comprises a plurality of memory allocators and in <FIG> one memory allocator <NUM> is shown. The other memory allocators are omitted from <FIG> for clarity. Each allocator is assigned a separate arena.

A message passing facility is implemented in the shared memory and comprises a plurality of queues, one queue <NUM> for each memory allocator <NUM>.

An allocator index <NUM> is provided. The allocator index <NUM> is a data structure used for finding memory allocators as it stores information about which allocator allocated which memory regions. In an example an allocator index <NUM> is an array indexed by the most significant bits of the virtual address of allocated memory regions. The most significant bits indicate the kind of the allocation and using the kind of the allocation it is possible to find metadata indicating the identity of the allocator, as an offset within the chunk. In another example an allocator index <NUM> is a map from the high bits of the virtual address of a hardware message passing facility of a supercomputer.

In some examples, such as the enclave example of <FIG>, a second allocator index <NUM> is present in the memory on the other side of the trust boundary and is kept in synchronization with the allocator index <NUM>.

In other examples, such as the library compartmentalization example of <FIG> there is no second allocator index and the allocator index <NUM> is a single canonical allocator index which has information about memory ranges available to each of the library compartments. Any updates to the canonical allocator index from unprivileged library compartments are proxied to the privileged code, which updates both the canonical allocator index and the library compartment's view (after validating updates). The allocator index is updated when a new chunk is allocated so that the cost of cross-trust boundary calls is amortized.

In the method of <FIG>, in which thread G from the first software component <NUM> allocates a memory region in the shared memory, the thread G has a local allocator <NUM> in the shared memory. The thread G is able to use the local allocator <NUM> to directly allocate an object in the shared memory without the need for making a cross trust boundary call.

In the method of <FIG>, in which thread R from the second software component <NUM> wants to deallocate a memory region M which was allocated by thread G, then thread R uses the allocator index <NUM> to identify which thread allocated memory region M. Thread R looks up a reference to memory region M in the allocator index <NUM> and finds the identity of the allocator which allocated memory region M. The allocator index <NUM> is populated with data available in the second software component without the need to make cross trust boundary calls.

In some, but not all examples, there is a second allocator index <NUM> outside the shared memory. The second allocator index potentially has information that is not known to the allocator index <NUM> in the shared memory region, because of the location of the second allocator index. However, a synchronization method is used to synchronize the allocator indexes. The synchronization method uses cross trust boundary calls but these are infrequent and so do not introduce undue burden and/or delay. In some examples, the cross trust boundary calls for synchronization are part of the same cross trust boundary call used to reserve a region of the shared memory for an allocator in the method of <FIG>. The second allocator index is used in the enclave example of <FIG>. When the method of <FIG> is performed by the program <NUM> outside the enclave, only the allocator index outside the enclave (allocator index <NUM>) is visible and so that is the only one checked. The program <NUM> running outside the enclave cannot see memory within the enclave <NUM> and so cannot free memory within the enclave <NUM>. When freeing memory from within the enclave <NUM>, a thread in the enclave carries out the method of <FIG>. It looks up in the second allocator index <NUM> and checks the other allocator index <NUM> if the memory is outside enclave range.

Thread R identifies (from the allocator index) that it was thread G which allocated M. Thread R then puts an entry into queue <NUM> of thread G's local allocator <NUM>. In order for thread R to put an entry into queue <NUM> of thread G's local allocator <NUM> atomic operations in memory are carried out but no cross trust boundary calls. Thus the process of putting entries in the queues is very cost effective. In a preferred example a plurality of requests to put entries in queue <NUM> are collected and sent in a batch to the queue <NUM> in order to give efficiency.

In the library compartmentalization example of <FIG> batching of requests to put entries in queue <NUM> causes problems when a library compartment exits. When the library compartment exits, then pointers to memory owned by it are to be freed before the memory can be unmapped. If the pointers to the memory owned by the library compartment are cached in other allocators (in the queues) the it takes an unbounded amount of time to free the pointers. Therefore, for deallocations of memory owned by untrusted code from trusted code the entries are added to the remote queue immediately in order to avoid the unbounded time.

The entry comprises a request to free memory region M. In an example each queue such as queue <NUM> is a multi-producer, single-consumer lockless queue. The queue is multi-producer since a plurality of different threads are able to put entries into the queue. The queue is single-consumer since a single allocator takes items from the queue. The queue is lock-less since no locks are placed on the queue whilst it is in operation.

In the method of <FIG>, in which thread G takes items from queue <NUM>, thread G takes items from the queue <NUM> which are memory regions for it to deallocate and it uses the local allocator <NUM> to deallocate those. Retrieving the messages from the queue is achieved in a single atomic operation and so is very efficient. Since local deallocator <NUM> is used there is no cross trust boundary call involved.

In the method of <FIG>, the thread G carries out validation. Thread G validates that memory access to memory owned by data structures related to memory allocation within the shared memory are within the shared memory. The validation is typically a simple range check that the memory accesses are within the shared range. If the shared memory region is not contiguous there are a plurality of ranges to check.

Previous allocators which support multiple threads typically use locks to lock data structures and then manipulate them in order to deal with contention. However, use of locks is problematic for the type of highly asynchronous, cross trust boundary situation of the present disclosure. One can't necessarily trust the lock implementation even if it is an in-memory spin lock. It would be very easy to maliciously lock the data structure and never unlock it, or unlock the data structure whilst it is being modified, or unlock the data structure whilst another entity is modifying it.

The in-memory message passing facility and the validation process of the disclosure operate in an unconventional manner to achieve memory deallocation across a trust boundary between two software components in an efficient manner.

The in-memory message passing facility and the validation process improve the functioning of the underlying computing device by enabling efficient deallocation of memory across a trust boundary between two software components.

<FIG> illustrates various components of an exemplary computing-based device <NUM> which are implemented as any form of a computing and/or electronic device, and in which embodiments of functionality for memory deallocation across a trust boundary are implemented in some examples.

Computing-based device <NUM> comprises one or more processors <NUM> which are microprocessors, controllers or any other suitable type of processors for processing computer executable instructions to control the operation of the device in order to deallocate memory across a trust boundary between two software components. In some examples, for example where a system on a chip architecture is used, the processors <NUM> include one or more fixed function blocks (also referred to as accelerators) which implement a part of the method of any of <FIG> in hardware (rather than software or firmware).

The computer executable instructions are provided using any computer-readable media that is accessible by computing based device <NUM>. Computer-readable media includes, for example, computer storage media such as memory <NUM> and communications media. Memory <NUM> stores two or more software components <NUM> separated by a trust boundary and memory <NUM> also stores message passing facility <NUM>.

Computer storage media, such as memory <NUM>, includes volatile and nonvolatile, removable and non-removable media implemented in any method or technology for storage of information such as computer readable instructions, data structures, program modules or the like. Computer storage media includes, but is not limited to, random access memory (RAM), read only memory (ROM), erasable programmable read only memory (EPROM), electronic erasable programmable read only memory (EEPROM), flash memory or other memory technology, compact disc read only memory (CD-ROM), digital versatile disks (DVD) or other optical storage, magnetic cassettes, magnetic tape, magnetic disk storage or other magnetic storage devices, or any other non-transmission medium that is used to store information for access by a computing device. In contrast, communication media embody computer readable instructions, data structures, program modules, or the like in a modulated data signal, such as a carrier wave, or other transport mechanism. As defined herein, computer storage media does not include communication media. Therefore, a computer storage medium should not be interpreted to be a propagating signal per se. Although the computer storage media (memory <NUM>) is shown within the computing-based device <NUM> it will be appreciated that the storage is, in some examples, distributed or located remotely and accessed via a network or other communication link (e.g. using communication interface <NUM>).

The computing-based device <NUM> also comprises an input/output interface <NUM> arranged to output display information to a display device which may be separate from or integral to the computing-based device <NUM>. The display information may provide a graphical user interface. The input/output controller interface <NUM> is also arranged to receive and process input from one or more devices, such as a user input device (e.g. a mouse, keyboard, camera, microphone or other sensor). In some examples the user input device detects voice input, user gestures or other user actions and provides a natural user interface (NUI). In an embodiment the display device also acts as the user input device if it is a touch sensitive display device. The input/output interface <NUM> outputs data to devices other than the display device in some examples, e.g. a locally connected printing device.

The term 'subset' is used herein to refer to a proper subset such that a subset of a set does not comprise all the elements of the set (i.e. at least one of the elements of the set is missing from the subset).

Claim 1:
A method of memory deallocation across a trust boundary between a first software component and at least a second software component, the method comprising:
sharing some memory (<NUM>) between the first and second software components (<NUM>), the shared memory having one or more shared memory ranges;
implementing an in-memory message passing facility (<NUM>) using the shared memory (<NUM>);
using the first software component to deallocate a memory allocation from the shared memory (<NUM>) which has been allocated by the second software component, by:
adding a request to free the memory allocation to a record in the in-memory message passing facility;
using the second software component, taking the request to free the memory allocation from the message passing facility (<NUM>);
using the second software component, freeing the memory allocation using a local deallocation mechanism while validating that memory accesses performed during the freeing of the allocation are within the shared memory (<NUM>) by making a range check of the one or more shared memory ranges.