mirror of
https://github.com/girlbossceo/hardened_malloc-rs.git
synced 2025-04-29 06:09:25 -04:00
initial commit of hardened_malloc-sys
Signed-off-by: girlbossceo <june@girlboss.ceo>
This commit is contained in:
commit
c8de762782
94 changed files with 10439 additions and 0 deletions
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
/target
|
||||||
|
.DS_Store
|
||||||
|
Cargo.lock
|
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
[submodule "hardened_malloc"]
|
||||||
|
path = "src/hardened_malloc"
|
||||||
|
url = https://github.com/GrapheneOS/hardened_malloc.git
|
134
CODE_OF_CONDUCT.md
Normal file
134
CODE_OF_CONDUCT.md
Normal file
|
@ -0,0 +1,134 @@
|
||||||
|
|
||||||
|
# Contributor Covenant Code of Conduct
|
||||||
|
|
||||||
|
## Our Pledge
|
||||||
|
|
||||||
|
We as members, contributors, and leaders pledge to make participation in our
|
||||||
|
community a harassment-free experience for everyone, regardless of age, body
|
||||||
|
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||||
|
identity and expression, level of experience, education, socio-economic status,
|
||||||
|
nationality, personal appearance, race, caste, color, religion, or sexual
|
||||||
|
identity and orientation.
|
||||||
|
|
||||||
|
We pledge to act and interact in ways that contribute to an open, welcoming,
|
||||||
|
diverse, inclusive, and healthy community.
|
||||||
|
|
||||||
|
## Our Standards
|
||||||
|
|
||||||
|
Examples of behavior that contributes to a positive environment for our
|
||||||
|
community include:
|
||||||
|
|
||||||
|
* Demonstrating empathy and kindness toward other people
|
||||||
|
* Being respectful of differing opinions, viewpoints, and experiences
|
||||||
|
* Giving and gracefully accepting constructive feedback
|
||||||
|
* Accepting responsibility and apologizing to those affected by our mistakes,
|
||||||
|
and learning from the experience
|
||||||
|
* Focusing on what is best not just for us as individuals, but for the overall
|
||||||
|
community
|
||||||
|
|
||||||
|
Examples of unacceptable behavior include:
|
||||||
|
|
||||||
|
* The use of sexualized language or imagery, and sexual attention or advances of
|
||||||
|
any kind
|
||||||
|
* Trolling, insulting or derogatory comments, and personal or political attacks
|
||||||
|
* Public or private harassment
|
||||||
|
* Publishing others' private information, such as a physical or email address,
|
||||||
|
without their explicit permission
|
||||||
|
* Other conduct which could reasonably be considered inappropriate in a
|
||||||
|
professional setting
|
||||||
|
|
||||||
|
## Enforcement Responsibilities
|
||||||
|
|
||||||
|
Community leaders are responsible for clarifying and enforcing our standards of
|
||||||
|
acceptable behavior and will take appropriate and fair corrective action in
|
||||||
|
response to any behavior that they deem inappropriate, threatening, offensive,
|
||||||
|
or harmful.
|
||||||
|
|
||||||
|
Community leaders have the right and responsibility to remove, edit, or reject
|
||||||
|
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||||
|
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
||||||
|
decisions when appropriate.
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
This Code of Conduct applies within all community spaces, and also applies when
|
||||||
|
an individual is officially representing the community in public spaces.
|
||||||
|
Examples of representing our community include using an official e-mail address,
|
||||||
|
posting via an official social media account, or acting as an appointed
|
||||||
|
representative at an online or offline event.
|
||||||
|
|
||||||
|
## Enforcement
|
||||||
|
|
||||||
|
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||||
|
reported to the community leaders responsible for enforcement at Email
|
||||||
|
[strawberry@pupbrain.dev] or via Matrix [@strawberry:puppygock.gay]
|
||||||
|
All complaints will be reviewed and investigated promptly and fairly.
|
||||||
|
|
||||||
|
All community leaders are obligated to respect the privacy and security of the
|
||||||
|
reporter of any incident.
|
||||||
|
|
||||||
|
## Enforcement Guidelines
|
||||||
|
|
||||||
|
Community leaders will follow these Community Impact Guidelines in determining
|
||||||
|
the consequences for any action they deem in violation of this Code of Conduct:
|
||||||
|
|
||||||
|
### 1. Correction
|
||||||
|
|
||||||
|
**Community Impact**: Use of inappropriate language or other behavior deemed
|
||||||
|
unprofessional or unwelcome in the community.
|
||||||
|
|
||||||
|
**Consequence**: A private, written warning from community leaders, providing
|
||||||
|
clarity around the nature of the violation and an explanation of why the
|
||||||
|
behavior was inappropriate. A public apology may be requested.
|
||||||
|
|
||||||
|
### 2. Warning
|
||||||
|
|
||||||
|
**Community Impact**: A violation through a single incident or series of
|
||||||
|
actions.
|
||||||
|
|
||||||
|
**Consequence**: A warning with consequences for continued behavior. No
|
||||||
|
interaction with the people involved, including unsolicited interaction with
|
||||||
|
those enforcing the Code of Conduct, for a specified period of time. This
|
||||||
|
includes avoiding interactions in community spaces as well as external channels
|
||||||
|
like social media. Violating these terms may lead to a temporary or permanent
|
||||||
|
ban.
|
||||||
|
|
||||||
|
### 3. Temporary Ban
|
||||||
|
|
||||||
|
**Community Impact**: A serious violation of community standards, including
|
||||||
|
sustained inappropriate behavior.
|
||||||
|
|
||||||
|
**Consequence**: A temporary ban from any sort of interaction or public
|
||||||
|
communication with the community for a specified period of time. No public or
|
||||||
|
private interaction with the people involved, including unsolicited interaction
|
||||||
|
with those enforcing the Code of Conduct, is allowed during this period.
|
||||||
|
Violating these terms may lead to a permanent ban.
|
||||||
|
|
||||||
|
### 4. Permanent Ban
|
||||||
|
|
||||||
|
**Community Impact**: Demonstrating a pattern of violation of community
|
||||||
|
standards, including sustained inappropriate behavior, harassment of an
|
||||||
|
individual, or aggression toward or disparagement of classes of individuals.
|
||||||
|
|
||||||
|
**Consequence**: A permanent ban from any sort of public interaction within the
|
||||||
|
community.
|
||||||
|
|
||||||
|
## Attribution
|
||||||
|
|
||||||
|
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||||
|
version 2.1, available at
|
||||||
|
[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
|
||||||
|
|
||||||
|
Community Impact Guidelines were inspired by
|
||||||
|
[Mozilla's code of conduct enforcement ladder][Mozilla CoC].
|
||||||
|
|
||||||
|
For answers to common questions about this code of conduct, see the FAQ at
|
||||||
|
[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at
|
||||||
|
[https://www.contributor-covenant.org/translations][translations].
|
||||||
|
|
||||||
|
[homepage]: https://www.contributor-covenant.org
|
||||||
|
[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
|
||||||
|
[Mozilla CoC]: https://github.com/mozilla/diversity
|
||||||
|
[FAQ]: https://www.contributor-covenant.org/faq
|
||||||
|
[translations]: https://www.contributor-covenant.org/translations
|
||||||
|
|
30
Cargo.toml
Normal file
30
Cargo.toml
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
[package]
|
||||||
|
name = "hardened_malloc-sys"
|
||||||
|
build = "build.rs"
|
||||||
|
description = "hardened_malloc rust wrapper (sys crate)"
|
||||||
|
authors = ["strawberry <strawberry@pupbrain.dev>"]
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
license = "Apache-2.0 and MIT"
|
||||||
|
repository = "https://github.com/girlbossceo/hardened_malloc-sys"
|
||||||
|
categories = ["api-bindings", "memory-management"]
|
||||||
|
keywords = ["hardened_malloc", "malloc", "hardened memory allocator", "security"]
|
||||||
|
readme = "README.md"
|
||||||
|
exclude = [
|
||||||
|
"/src/hardened_malloc/test",
|
||||||
|
"/src/hardened_malloc/androidtest",
|
||||||
|
"/src/hardened_malloc/out",
|
||||||
|
"/src/hardened_malloc/out-light",
|
||||||
|
]
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["light"]
|
||||||
|
light = []
|
||||||
|
# "standard" feature is "default.mk" config in hardened_malloc
|
||||||
|
standard = []
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
libc = "0.2"
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
cc = "1.0"
|
202
LICENCE
Normal file
202
LICENCE
Normal file
|
@ -0,0 +1,202 @@
|
||||||
|
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [2023] [June]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
19
LICENCE_GRAPHENEOS
Normal file
19
LICENCE_GRAPHENEOS
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
Copyright © 2018-2023 GrapheneOS
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
10
README.md
Normal file
10
README.md
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
# hardened_malloc-sys
|
||||||
|
|
||||||
|
the sys repo, rust wrapper
|
||||||
|
|
||||||
|
### TODO:
|
||||||
|
- [ ] test if this even works
|
||||||
|
- [ ] add support for explicit make config args on top of choosing variant
|
||||||
|
- [ ] make build script better overall
|
||||||
|
- [ ] support C preprocessor macro definitions
|
||||||
|
- [ ] add support for hardened_malloc's tests and our own tests
|
61
build.rs
Normal file
61
build.rs
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
use std::{env, process::Command, path::Path};
|
||||||
|
|
||||||
|
/// If submodules were not synced, sync them to actually build hardened_malloc
|
||||||
|
fn update_submodules() {
|
||||||
|
let program = "git";
|
||||||
|
let dir = "../";
|
||||||
|
let args = ["submodule", "update", "--init", "--recursive"];
|
||||||
|
println!(
|
||||||
|
"Running command: \"{} {}\" in directory: {}",
|
||||||
|
program,
|
||||||
|
args.join(" "),
|
||||||
|
dir
|
||||||
|
);
|
||||||
|
let ret = Command::new(program).current_dir(dir).args(args).status();
|
||||||
|
|
||||||
|
match ret.map(|status| (status.success(), status.code())) {
|
||||||
|
Ok((true, _)) => (),
|
||||||
|
Ok((false, Some(c))) => panic!("Command failed with error code {}", c),
|
||||||
|
Ok((false, None)) => panic!("Command exited with no error code, possibly killed by system"),
|
||||||
|
Err(e) => panic!("Command failed with error: {}", e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
if !Path::new("src/hardened_malloc/Makefile").exists() {
|
||||||
|
update_submodules();
|
||||||
|
}
|
||||||
|
let variant: &str;
|
||||||
|
|
||||||
|
if cfg!(feature = "light") {
|
||||||
|
variant = "light";
|
||||||
|
} else {
|
||||||
|
variant = "default";
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: handle support for explicit make flags like N_ARENA=1 and such
|
||||||
|
|
||||||
|
let mut make_command = Command::new("make");
|
||||||
|
let make_output = make_command
|
||||||
|
.current_dir("src/hardened_malloc/")
|
||||||
|
.env("V", "1") // always verbose mode for cargo
|
||||||
|
.env("VARIANT", variant)
|
||||||
|
.output()
|
||||||
|
.unwrap_or_else(|error| {
|
||||||
|
panic!("Failed to run 'make {}': ", error);
|
||||||
|
});
|
||||||
|
if !make_output.status.success() {
|
||||||
|
panic!(
|
||||||
|
"building hardened_malloc failed:\n{:?}\n{}\n{}",
|
||||||
|
make_command,
|
||||||
|
String::from_utf8_lossy(&make_output.stdout),
|
||||||
|
String::from_utf8_lossy(&make_output.stderr)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
//println!("cargo:rustc-link-search=native=src/hardened_malloc");
|
||||||
|
|
||||||
|
//println!("cargo:rerun-if-changed=build.rs");
|
||||||
|
println!("cargo:rerun-if-changed=src/hardened_malloc/");
|
||||||
|
//println!("cargo:out_dir={}", env::var("OUT_DIR").unwrap());
|
||||||
|
}
|
16
hardened_malloc_sources.txt
Normal file
16
hardened_malloc_sources.txt
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
src/hardened_malloc/chacha.c
|
||||||
|
src/hardened_malloc/h_malloc.c
|
||||||
|
src/hardened_malloc/memory.c
|
||||||
|
src/hardened_malloc/pages.c
|
||||||
|
src/hardened_malloc/random.c
|
||||||
|
src/hardened_malloc/util.c
|
||||||
|
src/hardened_malloc/arm_mte.h
|
||||||
|
src/hardened_malloc/chacha.h
|
||||||
|
src/hardened_malloc/memory.h
|
||||||
|
src/hardened_malloc/memtag.h
|
||||||
|
src/hardened_malloc/mutex.h
|
||||||
|
src/hardened_malloc/pages.h
|
||||||
|
src/hardened_malloc/random.h
|
||||||
|
src/hardened_malloc/util.h
|
||||||
|
src/hardened_malloc/new.cc
|
||||||
|
src/hardened_malloc/third_party/libdivide.h
|
2
src/hardened_malloc/.clang-tidy
Normal file
2
src/hardened_malloc/.clang-tidy
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
Checks: 'bugprone-*,-bugprone-easily-swappable-parameters,-bugprone-macro-parentheses,-bugprone-too-small-loop-variable,cert-*,-cert-err33-c,clang-analyzer-*,-clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling,-clang-diagnostic-constant-logical-operand,readability-*,-readability-function-cognitive-complexity,-readability-identifier-length,-readability-inconsistent-declaration-parameter-name,-readability-magic-numbers,-readability-named-parameter,llvm-include-order,misc-*'
|
||||||
|
WarningsAsErrors: '*'
|
7
src/hardened_malloc/.github/dependabot.yml
vendored
Normal file
7
src/hardened_malloc/.github/dependabot.yml
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
version: 2
|
||||||
|
updates:
|
||||||
|
- package-ecosystem: github-actions
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: daily
|
||||||
|
target-branch: main
|
53
src/hardened_malloc/.github/workflows/build-and-test.yml
vendored
Normal file
53
src/hardened_malloc/.github/workflows/build-and-test.yml
vendored
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
name: Build and run tests
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
pull_request:
|
||||||
|
schedule:
|
||||||
|
- cron: '0 2 * * *'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-ubuntu-gcc:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
version: [12]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Setting up gcc version
|
||||||
|
run: |
|
||||||
|
sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-${{ matrix.version }} 100
|
||||||
|
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-${{ matrix.version }} 100
|
||||||
|
- name: Build
|
||||||
|
run: make test
|
||||||
|
build-ubuntu-clang:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
version: [14, 15]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Setting up clang version
|
||||||
|
run: |
|
||||||
|
sudo update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-${{ matrix.version }} 100
|
||||||
|
sudo update-alternatives --install /usr/bin/clang clang /usr/bin/clang-${{ matrix.version }} 100
|
||||||
|
- name: Build
|
||||||
|
run: CC=clang CXX=clang++ make test
|
||||||
|
build-musl:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
container:
|
||||||
|
image: alpine:latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Install dependencies
|
||||||
|
run: apk update && apk add build-base python3
|
||||||
|
- name: Build
|
||||||
|
run: make test
|
||||||
|
build-ubuntu-gcc-aarch64:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Install dependencies
|
||||||
|
run: sudo apt-get update && sudo apt-get install -y --no-install-recommends gcc-aarch64-linux-gnu g++-aarch64-linux-gnu libgcc-s1-arm64-cross cpp-aarch64-linux-gnu
|
||||||
|
- name: Build
|
||||||
|
run: CC=aarch64-linux-gnu-gcc CXX=aarch64-linux-gnu-gcc++ make CONFIG_NATIVE=false
|
2
src/hardened_malloc/.gitignore
vendored
Normal file
2
src/hardened_malloc/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
out/
|
||||||
|
out-light/
|
83
src/hardened_malloc/Android.bp
Normal file
83
src/hardened_malloc/Android.bp
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
common_cflags = [
|
||||||
|
"-pipe",
|
||||||
|
"-O3",
|
||||||
|
//"-flto",
|
||||||
|
"-fPIC",
|
||||||
|
"-fvisibility=hidden",
|
||||||
|
//"-fno-plt",
|
||||||
|
"-Wall",
|
||||||
|
"-Wextra",
|
||||||
|
"-Wcast-align",
|
||||||
|
"-Wcast-qual",
|
||||||
|
"-Wwrite-strings",
|
||||||
|
"-Werror",
|
||||||
|
"-DH_MALLOC_PREFIX",
|
||||||
|
"-DZERO_ON_FREE=true",
|
||||||
|
"-DWRITE_AFTER_FREE_CHECK=true",
|
||||||
|
"-DSLOT_RANDOMIZE=true",
|
||||||
|
"-DSLAB_CANARY=true",
|
||||||
|
"-DSLAB_QUARANTINE_RANDOM_LENGTH=1",
|
||||||
|
"-DSLAB_QUARANTINE_QUEUE_LENGTH=1",
|
||||||
|
"-DCONFIG_EXTENDED_SIZE_CLASSES=true",
|
||||||
|
"-DCONFIG_LARGE_SIZE_CLASSES=true",
|
||||||
|
"-DGUARD_SLABS_INTERVAL=1",
|
||||||
|
"-DGUARD_SIZE_DIVISOR=2",
|
||||||
|
"-DREGION_QUARANTINE_RANDOM_LENGTH=256",
|
||||||
|
"-DREGION_QUARANTINE_QUEUE_LENGTH=1024",
|
||||||
|
"-DREGION_QUARANTINE_SKIP_THRESHOLD=33554432", // 32MiB
|
||||||
|
"-DFREE_SLABS_QUARANTINE_RANDOM_LENGTH=32",
|
||||||
|
"-DCONFIG_CLASS_REGION_SIZE=34359738368", // 32GiB
|
||||||
|
"-DN_ARENA=1",
|
||||||
|
"-DCONFIG_STATS=true",
|
||||||
|
"-DCONFIG_SELF_INIT=false",
|
||||||
|
]
|
||||||
|
|
||||||
|
cc_defaults {
|
||||||
|
name: "hardened_malloc_defaults",
|
||||||
|
defaults: ["linux_bionic_supported"],
|
||||||
|
cflags: common_cflags,
|
||||||
|
conlyflags: ["-std=c17", "-Wmissing-prototypes"],
|
||||||
|
stl: "none",
|
||||||
|
}
|
||||||
|
|
||||||
|
lib_src_files = [
|
||||||
|
"chacha.c",
|
||||||
|
"h_malloc.c",
|
||||||
|
"memory.c",
|
||||||
|
"pages.c",
|
||||||
|
"random.c",
|
||||||
|
"util.c",
|
||||||
|
]
|
||||||
|
|
||||||
|
cc_library {
|
||||||
|
name: "libhardened_malloc",
|
||||||
|
ramdisk_available: true,
|
||||||
|
vendor_ramdisk_available: true,
|
||||||
|
recovery_available: true,
|
||||||
|
defaults: ["hardened_malloc_defaults"],
|
||||||
|
srcs: lib_src_files,
|
||||||
|
export_include_dirs: ["include"],
|
||||||
|
static_libs: ["libasync_safe"],
|
||||||
|
target: {
|
||||||
|
android: {
|
||||||
|
shared: {
|
||||||
|
enabled: false,
|
||||||
|
},
|
||||||
|
system_shared_libs: [],
|
||||||
|
},
|
||||||
|
linux_bionic: {
|
||||||
|
system_shared_libs: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
product_variables: {
|
||||||
|
debuggable: {
|
||||||
|
cflags: ["-DLABEL_MEMORY"],
|
||||||
|
},
|
||||||
|
device_has_arm_mte: {
|
||||||
|
cflags: ["-DHAS_ARM_MTE", "-march=armv9-a+memtag"]
|
||||||
|
},
|
||||||
|
},
|
||||||
|
apex_available: [
|
||||||
|
"com.android.runtime",
|
||||||
|
],
|
||||||
|
}
|
283
src/hardened_malloc/CREDITS
Normal file
283
src/hardened_malloc/CREDITS
Normal file
|
@ -0,0 +1,283 @@
|
||||||
|
chacha.c is a simple conversion of chacha-merged.c to a keystream-only implementation:
|
||||||
|
|
||||||
|
chacha-merged.c version 20080118
|
||||||
|
D. J. Bernstein
|
||||||
|
Public domain.
|
||||||
|
|
||||||
|
h_malloc.c open-addressed hash table (regions_grow, regions_insert, regions_find, regions_delete):
|
||||||
|
|
||||||
|
Copyright (c) 2008, 2010, 2011, 2016 Otto Moerbeek <otto@drijf.net>
|
||||||
|
Copyright (c) 2012 Matthew Dempsky <matthew@openbsd.org>
|
||||||
|
Copyright (c) 2008 Damien Miller <djm@openbsd.org>
|
||||||
|
Copyright (c) 2000 Poul-Henning Kamp <phk@FreeBSD.org>
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
|
copyright notice and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
|
libdivide:
|
||||||
|
|
||||||
|
Copyright (C) 2010 - 2019 ridiculous_fish, <libdivide@ridiculousfish.com>
|
||||||
|
Copyright (C) 2016 - 2019 Kim Walisch, <kim.walisch@gmail.com>
|
||||||
|
|
||||||
|
Boost Software License - Version 1.0 - August 17th, 2003
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person or organization
|
||||||
|
obtaining a copy of the software and accompanying documentation covered by
|
||||||
|
this license (the "Software") to use, reproduce, display, distribute,
|
||||||
|
execute, and transmit the Software, and to prepare derivative works of the
|
||||||
|
Software, and to permit third-parties to whom the Software is furnished to
|
||||||
|
do so, all subject to the following:
|
||||||
|
|
||||||
|
The copyright notices in the Software and this entire statement, including
|
||||||
|
the above license grant, this restriction and the following disclaimer,
|
||||||
|
must be included in all copies of the Software, in whole or in part, and
|
||||||
|
all derivative works of the Software, unless such copies or derivative
|
||||||
|
works are solely in the form of machine-executable object code generated by
|
||||||
|
a source language processor.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||||
|
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||||
|
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||||
|
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
random.c get_random_{type}_uniform functions are based on Fast Random Integer
|
||||||
|
Generation in an Interval by Daniel Lemire
|
||||||
|
|
||||||
|
arm_mte.h arm_mte_tag_and_clear_mem function contents were copied from storeTags function in scudo:
|
||||||
|
|
||||||
|
==============================================================================
|
||||||
|
The LLVM Project is under the Apache License v2.0 with LLVM Exceptions:
|
||||||
|
==============================================================================
|
||||||
|
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
|
||||||
|
---- LLVM Exceptions to the Apache 2.0 License ----
|
||||||
|
|
||||||
|
As an exception, if, as a result of your compiling your source code, portions
|
||||||
|
of this Software are embedded into an Object form of such source code, you
|
||||||
|
may redistribute such embedded portions in such Object form without complying
|
||||||
|
with the conditions of Sections 4(a), 4(b) and 4(d) of the License.
|
||||||
|
|
||||||
|
In addition, if you combine or link compiled forms of this Software with
|
||||||
|
software that is licensed under the GPLv2 ("Combined Software") and if a
|
||||||
|
court of competent jurisdiction determines that the patent provision (Section
|
||||||
|
3), the indemnity provision (Section 9) or other Section of the License
|
||||||
|
conflicts with the conditions of the GPLv2, you may retroactively and
|
||||||
|
prospectively choose to deem waived or otherwise exclude such Section(s) of
|
||||||
|
the License, but only in their entirety and only with respect to the Combined
|
||||||
|
Software.
|
||||||
|
|
||||||
|
==============================================================================
|
35
src/hardened_malloc/KERNEL_FEATURE_WISHLIST.md
Normal file
35
src/hardened_malloc/KERNEL_FEATURE_WISHLIST.md
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
Very important and should be an easy sell:
|
||||||
|
|
||||||
|
* improved robustness for high vma count on high memory machines
|
||||||
|
* much higher `vm.max_map_count` by default
|
||||||
|
* work on improving performance and resource usage with high vma count
|
||||||
|
* add a way to disable the brk heap and have mmap grow upwards like it did in
|
||||||
|
the past (preserving the same high base entropy)
|
||||||
|
|
||||||
|
Somewhat important and an easy sell:
|
||||||
|
|
||||||
|
* alternative to `RLIMIT_AS` for accountable mappings only
|
||||||
|
* memory control groups are sometimes a better option but there are still
|
||||||
|
users of `RLIMIT_AS` that are problematic for mitigations or simply fast
|
||||||
|
garbage collector implementations, etc. mapping lots of `PROT_NONE` memory
|
||||||
|
* mremap flag to disable unmapping the source mapping
|
||||||
|
* also needed by jemalloc for different reasons
|
||||||
|
* not needed if the kernel gets first class support for arbitrarily sized
|
||||||
|
guard pages and a virtual memory quarantine feature
|
||||||
|
* `MREMAP_DONTUNMAP` is now available but doesn't support expanding the
|
||||||
|
mapping which may be an issue due to VMA merging being unreliable
|
||||||
|
|
||||||
|
Fairly infeasible to land but could reduce overhead and extend coverage of
|
||||||
|
security features to other code directly using mmap:
|
||||||
|
|
||||||
|
* first class support for arbitrarily sized guard pages for mmap and mremap to
|
||||||
|
eliminate half of the resulting VMAs and reduce 2 system calls to 1
|
||||||
|
* not usable if it doesn't support mremap (shrink, grow, grow via move)
|
||||||
|
* not usable if the guard page size is static
|
||||||
|
* should support changing guard size for mremap growth via move
|
||||||
|
* must be possible to set it up from the process
|
||||||
|
* virtual memory quarantine
|
||||||
|
* must be possible to set it up from the process
|
||||||
|
* first-class support for aligned mappings with mmap and ideally mremap
|
||||||
|
* not usable unless guard page support is provided and of course it has to
|
||||||
|
work with this too
|
19
src/hardened_malloc/LICENSE
Normal file
19
src/hardened_malloc/LICENSE
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
Copyright © 2018-2023 GrapheneOS
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
148
src/hardened_malloc/Makefile
Normal file
148
src/hardened_malloc/Makefile
Normal file
|
@ -0,0 +1,148 @@
|
||||||
|
VARIANT := default
|
||||||
|
|
||||||
|
ifneq ($(VARIANT),)
|
||||||
|
CONFIG_FILE := config/$(VARIANT).mk
|
||||||
|
include config/$(VARIANT).mk
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ($(VARIANT),default)
|
||||||
|
SUFFIX :=
|
||||||
|
else
|
||||||
|
SUFFIX := -$(VARIANT)
|
||||||
|
endif
|
||||||
|
|
||||||
|
OUT := out$(SUFFIX)
|
||||||
|
|
||||||
|
define safe_flag
|
||||||
|
$(shell $(CC) $(if $(filter clang%,$(CC)),-Werror=unknown-warning-option) -E $1 - </dev/null >/dev/null 2>&1 && echo $1 || echo $2)
|
||||||
|
endef
|
||||||
|
|
||||||
|
CPPFLAGS := $(CPPFLAGS) -D_GNU_SOURCE -I include
|
||||||
|
SHARED_FLAGS := -pipe -O3 -flto -fPIC -fvisibility=hidden -fno-plt \
|
||||||
|
$(call safe_flag,-fstack-clash-protection) $(call safe_flag,-fcf-protection) -fstack-protector-strong \
|
||||||
|
-Wall -Wextra $(call safe_flag,-Wcast-align=strict,-Wcast-align) -Wcast-qual -Wwrite-strings \
|
||||||
|
-Wundef
|
||||||
|
|
||||||
|
ifeq ($(CONFIG_WERROR),true)
|
||||||
|
SHARED_FLAGS += -Werror
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ($(CONFIG_NATIVE),true)
|
||||||
|
SHARED_FLAGS += -march=native
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ($(CONFIG_UBSAN),true)
|
||||||
|
SHARED_FLAGS += -fsanitize=undefined -fno-sanitize-recover=undefined
|
||||||
|
endif
|
||||||
|
|
||||||
|
CFLAGS := $(CFLAGS) -std=c17 $(SHARED_FLAGS) -Wmissing-prototypes -Wstrict-prototypes
|
||||||
|
CXXFLAGS := $(CXXFLAGS) -std=c++17 -fsized-deallocation $(SHARED_FLAGS)
|
||||||
|
LDFLAGS := $(LDFLAGS) -Wl,-O1,--as-needed,-z,defs,-z,relro,-z,now,-z,nodlopen,-z,text
|
||||||
|
|
||||||
|
SOURCES := chacha.c h_malloc.c memory.c pages.c random.c util.c
|
||||||
|
OBJECTS := $(SOURCES:.c=.o)
|
||||||
|
|
||||||
|
ifeq ($(CONFIG_CXX_ALLOCATOR),true)
|
||||||
|
# make sure LTO is compatible in case CC and CXX don't match (such as clang and g++)
|
||||||
|
CXX := $(CC)
|
||||||
|
LDLIBS += -lstdc++
|
||||||
|
|
||||||
|
SOURCES += new.cc
|
||||||
|
OBJECTS += new.o
|
||||||
|
endif
|
||||||
|
|
||||||
|
OBJECTS := $(addprefix $(OUT)/,$(OBJECTS))
|
||||||
|
|
||||||
|
ifeq (,$(filter $(CONFIG_SEAL_METADATA),true false))
|
||||||
|
$(error CONFIG_SEAL_METADATA must be true or false)
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq (,$(filter $(CONFIG_ZERO_ON_FREE),true false))
|
||||||
|
$(error CONFIG_ZERO_ON_FREE must be true or false)
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq (,$(filter $(CONFIG_WRITE_AFTER_FREE_CHECK),true false))
|
||||||
|
$(error CONFIG_WRITE_AFTER_FREE_CHECK must be true or false)
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq (,$(filter $(CONFIG_SLOT_RANDOMIZE),true false))
|
||||||
|
$(error CONFIG_SLOT_RANDOMIZE must be true or false)
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq (,$(filter $(CONFIG_SLAB_CANARY),true false))
|
||||||
|
$(error CONFIG_SLAB_CANARY must be true or false)
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq (,$(filter $(CONFIG_EXTENDED_SIZE_CLASSES),true false))
|
||||||
|
$(error CONFIG_EXTENDED_SIZE_CLASSES must be true or false)
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq (,$(filter $(CONFIG_LARGE_SIZE_CLASSES),true false))
|
||||||
|
$(error CONFIG_LARGE_SIZE_CLASSES must be true or false)
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq (,$(filter $(CONFIG_STATS),true false))
|
||||||
|
$(error CONFIG_STATS must be true or false)
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq (,$(filter $(CONFIG_SELF_INIT),true false))
|
||||||
|
$(error CONFIG_SELF_INIT must be true or false)
|
||||||
|
endif
|
||||||
|
|
||||||
|
CPPFLAGS += \
|
||||||
|
-DCONFIG_SEAL_METADATA=$(CONFIG_SEAL_METADATA) \
|
||||||
|
-DZERO_ON_FREE=$(CONFIG_ZERO_ON_FREE) \
|
||||||
|
-DWRITE_AFTER_FREE_CHECK=$(CONFIG_WRITE_AFTER_FREE_CHECK) \
|
||||||
|
-DSLOT_RANDOMIZE=$(CONFIG_SLOT_RANDOMIZE) \
|
||||||
|
-DSLAB_CANARY=$(CONFIG_SLAB_CANARY) \
|
||||||
|
-DSLAB_QUARANTINE_RANDOM_LENGTH=$(CONFIG_SLAB_QUARANTINE_RANDOM_LENGTH) \
|
||||||
|
-DSLAB_QUARANTINE_QUEUE_LENGTH=$(CONFIG_SLAB_QUARANTINE_QUEUE_LENGTH) \
|
||||||
|
-DCONFIG_EXTENDED_SIZE_CLASSES=$(CONFIG_EXTENDED_SIZE_CLASSES) \
|
||||||
|
-DCONFIG_LARGE_SIZE_CLASSES=$(CONFIG_LARGE_SIZE_CLASSES) \
|
||||||
|
-DGUARD_SLABS_INTERVAL=$(CONFIG_GUARD_SLABS_INTERVAL) \
|
||||||
|
-DGUARD_SIZE_DIVISOR=$(CONFIG_GUARD_SIZE_DIVISOR) \
|
||||||
|
-DREGION_QUARANTINE_RANDOM_LENGTH=$(CONFIG_REGION_QUARANTINE_RANDOM_LENGTH) \
|
||||||
|
-DREGION_QUARANTINE_QUEUE_LENGTH=$(CONFIG_REGION_QUARANTINE_QUEUE_LENGTH) \
|
||||||
|
-DREGION_QUARANTINE_SKIP_THRESHOLD=$(CONFIG_REGION_QUARANTINE_SKIP_THRESHOLD) \
|
||||||
|
-DFREE_SLABS_QUARANTINE_RANDOM_LENGTH=$(CONFIG_FREE_SLABS_QUARANTINE_RANDOM_LENGTH) \
|
||||||
|
-DCONFIG_CLASS_REGION_SIZE=$(CONFIG_CLASS_REGION_SIZE) \
|
||||||
|
-DN_ARENA=$(CONFIG_N_ARENA) \
|
||||||
|
-DCONFIG_STATS=$(CONFIG_STATS) \
|
||||||
|
-DCONFIG_SELF_INIT=$(CONFIG_SELF_INIT)
|
||||||
|
|
||||||
|
$(OUT)/libhardened_malloc$(SUFFIX).so: $(OBJECTS) | $(OUT)
|
||||||
|
$(CC) $(CFLAGS) $(LDFLAGS) -shared $^ $(LDLIBS) -o $@
|
||||||
|
|
||||||
|
$(OUT):
|
||||||
|
mkdir -p $(OUT)
|
||||||
|
|
||||||
|
$(OUT)/chacha.o: chacha.c chacha.h util.h $(CONFIG_FILE) | $(OUT)
|
||||||
|
$(COMPILE.c) $(OUTPUT_OPTION) $<
|
||||||
|
$(OUT)/h_malloc.o: h_malloc.c include/h_malloc.h mutex.h memory.h pages.h random.h util.h $(CONFIG_FILE) | $(OUT)
|
||||||
|
$(COMPILE.c) $(OUTPUT_OPTION) $<
|
||||||
|
$(OUT)/memory.o: memory.c memory.h util.h $(CONFIG_FILE) | $(OUT)
|
||||||
|
$(COMPILE.c) $(OUTPUT_OPTION) $<
|
||||||
|
$(OUT)/new.o: new.cc include/h_malloc.h util.h $(CONFIG_FILE) | $(OUT)
|
||||||
|
$(COMPILE.cc) $(OUTPUT_OPTION) $<
|
||||||
|
$(OUT)/pages.o: pages.c pages.h memory.h util.h $(CONFIG_FILE) | $(OUT)
|
||||||
|
$(COMPILE.c) $(OUTPUT_OPTION) $<
|
||||||
|
$(OUT)/random.o: random.c random.h chacha.h util.h $(CONFIG_FILE) | $(OUT)
|
||||||
|
$(COMPILE.c) $(OUTPUT_OPTION) $<
|
||||||
|
$(OUT)/util.o: util.c util.h $(CONFIG_FILE) | $(OUT)
|
||||||
|
$(COMPILE.c) $(OUTPUT_OPTION) $<
|
||||||
|
|
||||||
|
check: tidy
|
||||||
|
|
||||||
|
tidy:
|
||||||
|
clang-tidy --extra-arg=-std=c17 $(filter %.c,$(SOURCES)) -- $(CPPFLAGS)
|
||||||
|
clang-tidy --extra-arg=-std=c++17 $(filter %.cc,$(SOURCES)) -- $(CPPFLAGS)
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f $(OUT)/libhardened_malloc.so $(OBJECTS)
|
||||||
|
$(MAKE) -C test/ clean
|
||||||
|
|
||||||
|
test: $(OUT)/libhardened_malloc$(SUFFIX).so
|
||||||
|
$(MAKE) -C test/
|
||||||
|
python3 -m unittest discover --start-directory test/
|
||||||
|
|
||||||
|
.PHONY: check clean tidy test
|
1037
src/hardened_malloc/README.md
Normal file
1037
src/hardened_malloc/README.md
Normal file
File diff suppressed because it is too large
Load diff
25
src/hardened_malloc/androidtest/Android.bp
Normal file
25
src/hardened_malloc/androidtest/Android.bp
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
java_test_host {
|
||||||
|
name: "HMallocTest",
|
||||||
|
srcs: [
|
||||||
|
"src/**/*.java",
|
||||||
|
],
|
||||||
|
|
||||||
|
libs: [
|
||||||
|
"tradefed",
|
||||||
|
"compatibility-tradefed",
|
||||||
|
"compatibility-host-util",
|
||||||
|
],
|
||||||
|
|
||||||
|
static_libs: [
|
||||||
|
"cts-host-utils",
|
||||||
|
"frameworks-base-hostutils",
|
||||||
|
],
|
||||||
|
|
||||||
|
test_suites: [
|
||||||
|
"general-tests",
|
||||||
|
],
|
||||||
|
|
||||||
|
data_device_bins_64: [
|
||||||
|
"memtag_test",
|
||||||
|
],
|
||||||
|
}
|
13
src/hardened_malloc/androidtest/AndroidTest.xml
Normal file
13
src/hardened_malloc/androidtest/AndroidTest.xml
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<configuration description="hardened_malloc test">
|
||||||
|
|
||||||
|
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
|
||||||
|
<option name="cleanup" value="true" />
|
||||||
|
<option name="push" value="memtag_test->/data/local/tmp/memtag_test" />
|
||||||
|
</target_preparer>
|
||||||
|
|
||||||
|
<test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
|
||||||
|
<option name="jar" value="HMallocTest.jar" />
|
||||||
|
</test>
|
||||||
|
|
||||||
|
</configuration>
|
16
src/hardened_malloc/androidtest/memtag/Android.bp
Normal file
16
src/hardened_malloc/androidtest/memtag/Android.bp
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
cc_test {
|
||||||
|
name: "memtag_test",
|
||||||
|
srcs: ["memtag_test.cc"],
|
||||||
|
cflags: [
|
||||||
|
"-Wall",
|
||||||
|
"-Werror",
|
||||||
|
"-Wextra",
|
||||||
|
"-O0",
|
||||||
|
],
|
||||||
|
|
||||||
|
compile_multilib: "64",
|
||||||
|
|
||||||
|
sanitize: {
|
||||||
|
memtag_heap: true,
|
||||||
|
},
|
||||||
|
}
|
297
src/hardened_malloc/androidtest/memtag/memtag_test.cc
Normal file
297
src/hardened_malloc/androidtest/memtag/memtag_test.cc
Normal file
|
@ -0,0 +1,297 @@
|
||||||
|
// needed to uncondionally enable assertions
|
||||||
|
#undef NDEBUG
|
||||||
|
#include <assert.h>
|
||||||
|
#include <malloc.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <sys/utsname.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <set>
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
using u8 = uint8_t;
|
||||||
|
using uptr = uintptr_t;
|
||||||
|
using u64 = uint64_t;
|
||||||
|
|
||||||
|
const size_t DEFAULT_ALLOC_SIZE = 8;
|
||||||
|
const size_t CANARY_SIZE = 8;
|
||||||
|
|
||||||
|
void do_context_switch() {
|
||||||
|
utsname s;
|
||||||
|
uname(&s);
|
||||||
|
}
|
||||||
|
|
||||||
|
u8 get_pointer_tag(void *ptr) {
|
||||||
|
return (((uptr) ptr) >> 56) & 0xf;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *untag_pointer(void *ptr) {
|
||||||
|
const uintptr_t mask = UINTPTR_MAX >> 8;
|
||||||
|
return (void *) ((uintptr_t) ptr & mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This test checks that slab slot allocation uses tag that is distint from tags of its neighbors
|
||||||
|
// and from the tag of the previous allocation that used the same slot
|
||||||
|
void tag_distinctness() {
|
||||||
|
// 0 and 15 are reserved
|
||||||
|
const int min_tag = 1;
|
||||||
|
const int max_tag = 14;
|
||||||
|
|
||||||
|
struct SizeClass {
|
||||||
|
int size;
|
||||||
|
int slot_cnt;
|
||||||
|
};
|
||||||
|
|
||||||
|
// values from size_classes[] and size_class_slots[] in h_malloc.c
|
||||||
|
SizeClass size_classes[] = {
|
||||||
|
{ .size = 16, .slot_cnt = 256, },
|
||||||
|
{ .size = 32, .slot_cnt = 128, },
|
||||||
|
// this size class is used by allocations that are made by the addr_tag_map, which breaks
|
||||||
|
// tag distinctess checks
|
||||||
|
// { .size = 48, .slot_cnt = 85, },
|
||||||
|
{ .size = 64, .slot_cnt = 64, },
|
||||||
|
{ .size = 80, .slot_cnt = 51, },
|
||||||
|
{ .size = 96, .slot_cnt = 42, },
|
||||||
|
{ .size = 112, .slot_cnt = 36, },
|
||||||
|
{ .size = 128, .slot_cnt = 64, },
|
||||||
|
{ .size = 160, .slot_cnt = 51, },
|
||||||
|
{ .size = 192, .slot_cnt = 64, },
|
||||||
|
{ .size = 224, .slot_cnt = 54, },
|
||||||
|
{ .size = 10240, .slot_cnt = 6, },
|
||||||
|
{ .size = 20480, .slot_cnt = 1, },
|
||||||
|
};
|
||||||
|
|
||||||
|
int tag_usage[max_tag + 1];
|
||||||
|
|
||||||
|
for (size_t sc_idx = 0; sc_idx < sizeof(size_classes) / sizeof(SizeClass); ++sc_idx) {
|
||||||
|
SizeClass &sc = size_classes[sc_idx];
|
||||||
|
|
||||||
|
const size_t full_alloc_size = sc.size;
|
||||||
|
const size_t alloc_size = full_alloc_size - CANARY_SIZE;
|
||||||
|
|
||||||
|
// "tdc" is short for "tag distinctness check"
|
||||||
|
int left_neighbor_tdc_cnt = 0;
|
||||||
|
int right_neighbor_tdc_cnt = 0;
|
||||||
|
int prev_alloc_tdc_cnt = 0;
|
||||||
|
|
||||||
|
int iter_cnt = 600;
|
||||||
|
|
||||||
|
unordered_map<uptr, u8> addr_tag_map;
|
||||||
|
addr_tag_map.reserve(iter_cnt * sc.slot_cnt);
|
||||||
|
|
||||||
|
u64 seen_tags = 0;
|
||||||
|
|
||||||
|
for (int iter = 0; iter < iter_cnt; ++iter) {
|
||||||
|
uptr allocations[256]; // 256 is max slot count
|
||||||
|
|
||||||
|
for (int i = 0; i < sc.slot_cnt; ++i) {
|
||||||
|
u8 *p = (u8 *) malloc(alloc_size);
|
||||||
|
assert(p);
|
||||||
|
uptr addr = (uptr) untag_pointer(p);
|
||||||
|
u8 tag = get_pointer_tag(p);
|
||||||
|
|
||||||
|
assert(tag >= min_tag && tag <= max_tag);
|
||||||
|
seen_tags |= 1 << tag;
|
||||||
|
++tag_usage[tag];
|
||||||
|
|
||||||
|
// check most recent tags of left and right neighbors
|
||||||
|
|
||||||
|
auto left = addr_tag_map.find(addr - full_alloc_size);
|
||||||
|
if (left != addr_tag_map.end()) {
|
||||||
|
assert(left->second != tag);
|
||||||
|
++left_neighbor_tdc_cnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto right = addr_tag_map.find(addr + full_alloc_size);
|
||||||
|
if (right != addr_tag_map.end()) {
|
||||||
|
assert(right->second != tag);
|
||||||
|
++right_neighbor_tdc_cnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check previous tag of this slot
|
||||||
|
auto prev = addr_tag_map.find(addr);
|
||||||
|
if (prev != addr_tag_map.end()) {
|
||||||
|
assert(prev->second != tag);
|
||||||
|
++prev_alloc_tdc_cnt;
|
||||||
|
addr_tag_map.erase(addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
addr_tag_map.emplace(addr, tag);
|
||||||
|
|
||||||
|
for (size_t j = 0; j < alloc_size; ++j) {
|
||||||
|
// check that slot is zeroed
|
||||||
|
assert(p[j] == 0);
|
||||||
|
// check that slot is readable and writable
|
||||||
|
p[j]++;
|
||||||
|
}
|
||||||
|
|
||||||
|
allocations[i] = addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// free some of allocations to allow their slots to be reused
|
||||||
|
for (int i = sc.slot_cnt - 1; i >= 0; i -= 2) {
|
||||||
|
free((void *) allocations[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check that all of the tags were used, except reserved ones
|
||||||
|
assert(seen_tags == (0xffff & ~(1 << 0 | 1 << 15)));
|
||||||
|
|
||||||
|
printf("size_class\t%i\t" "tdc_left %i\t" "tdc_right %i\t" "tdc_prev_alloc %i\n",
|
||||||
|
sc.size, left_neighbor_tdc_cnt, right_neighbor_tdc_cnt, prev_alloc_tdc_cnt);
|
||||||
|
|
||||||
|
// make sure tag distinctess checks were actually performed
|
||||||
|
int min_tdc_cnt = sc.slot_cnt * iter_cnt / 5;
|
||||||
|
|
||||||
|
assert(prev_alloc_tdc_cnt > min_tdc_cnt);
|
||||||
|
|
||||||
|
if (sc.slot_cnt > 1) {
|
||||||
|
assert(left_neighbor_tdc_cnt > min_tdc_cnt);
|
||||||
|
assert(right_neighbor_tdc_cnt > min_tdc_cnt);
|
||||||
|
}
|
||||||
|
|
||||||
|
// async tag check failures are reported on context switch
|
||||||
|
do_context_switch();
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("\nTag use counters:\n");
|
||||||
|
|
||||||
|
int min = INT_MAX;
|
||||||
|
int max = 0;
|
||||||
|
double geomean = 0.0;
|
||||||
|
for (int i = min_tag; i <= max_tag; ++i) {
|
||||||
|
int v = tag_usage[i];
|
||||||
|
geomean += log(v);
|
||||||
|
min = std::min(min, v);
|
||||||
|
max = std::max(max, v);
|
||||||
|
printf("%i\t%i\n", i, tag_usage[i]);
|
||||||
|
}
|
||||||
|
int tag_cnt = 1 + max_tag - min_tag;
|
||||||
|
geomean = exp(geomean / tag_cnt);
|
||||||
|
|
||||||
|
double max_deviation = std::max((double) max - geomean, geomean - min);
|
||||||
|
|
||||||
|
printf("geomean: %.2f, max deviation from geomean: %.2f%%\n", geomean, (100.0 * max_deviation) / geomean);
|
||||||
|
}
|
||||||
|
|
||||||
|
u8* alloc_default() {
|
||||||
|
const size_t full_alloc_size = DEFAULT_ALLOC_SIZE + CANARY_SIZE;
|
||||||
|
set<uptr> addrs;
|
||||||
|
|
||||||
|
// make sure allocation has both left and right neighbors, otherwise overflow/underflow tests
|
||||||
|
// will fail when allocation is at the end/beginning of slab
|
||||||
|
for (;;) {
|
||||||
|
u8 *p = (u8 *) malloc(DEFAULT_ALLOC_SIZE);
|
||||||
|
assert(p);
|
||||||
|
|
||||||
|
uptr addr = (uptr) untag_pointer(p);
|
||||||
|
uptr left = addr - full_alloc_size;
|
||||||
|
if (addrs.find(left) != addrs.end()) {
|
||||||
|
uptr right = addr + full_alloc_size;
|
||||||
|
if (addrs.find(right) != addrs.end()) {
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addrs.emplace(addr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
volatile u8 u8_var;
|
||||||
|
|
||||||
|
void read_after_free() {
|
||||||
|
u8 *p = alloc_default();
|
||||||
|
free(p);
|
||||||
|
volatile u8 v = p[0];
|
||||||
|
(void) v;
|
||||||
|
}
|
||||||
|
|
||||||
|
void write_after_free() {
|
||||||
|
u8 *p = alloc_default();
|
||||||
|
free(p);
|
||||||
|
p[0] = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void underflow_read() {
|
||||||
|
u8 *p = alloc_default();
|
||||||
|
volatile u8 v = p[-1];
|
||||||
|
(void) v;
|
||||||
|
}
|
||||||
|
|
||||||
|
void underflow_write() {
|
||||||
|
u8 *p = alloc_default();
|
||||||
|
p[-1] = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void overflow_read() {
|
||||||
|
u8 *p = alloc_default();
|
||||||
|
volatile u8 v = p[DEFAULT_ALLOC_SIZE + CANARY_SIZE];
|
||||||
|
(void) v;
|
||||||
|
}
|
||||||
|
|
||||||
|
void overflow_write() {
|
||||||
|
u8 *p = alloc_default();
|
||||||
|
p[DEFAULT_ALLOC_SIZE + CANARY_SIZE] = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void untagged_read() {
|
||||||
|
u8 *p = alloc_default();
|
||||||
|
p = (u8 *) untag_pointer(p);
|
||||||
|
volatile u8 v = p[0];
|
||||||
|
(void) v;
|
||||||
|
}
|
||||||
|
|
||||||
|
void untagged_write() {
|
||||||
|
u8 *p = alloc_default();
|
||||||
|
p = (u8 *) untag_pointer(p);
|
||||||
|
p[0] = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
map<string, function<void()>> tests = {
|
||||||
|
#define TEST(s) { #s, s }
|
||||||
|
TEST(tag_distinctness),
|
||||||
|
TEST(read_after_free),
|
||||||
|
TEST(write_after_free),
|
||||||
|
TEST(overflow_read),
|
||||||
|
TEST(overflow_write),
|
||||||
|
TEST(underflow_read),
|
||||||
|
TEST(underflow_write),
|
||||||
|
TEST(untagged_read),
|
||||||
|
TEST(untagged_write),
|
||||||
|
#undef TEST
|
||||||
|
};
|
||||||
|
|
||||||
|
void segv_handler(int, siginfo_t *si, void *) {
|
||||||
|
fprintf(stderr, "SEGV_CODE %i", si->si_code);
|
||||||
|
exit(139); // standard exit code for SIGSEGV
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
setbuf(stdout, NULL);
|
||||||
|
assert(argc == 2);
|
||||||
|
|
||||||
|
auto test_name = string(argv[1]);
|
||||||
|
auto test_fn = tests[test_name];
|
||||||
|
assert(test_fn != nullptr);
|
||||||
|
|
||||||
|
assert(mallopt(M_BIONIC_SET_HEAP_TAGGING_LEVEL, M_HEAP_TAGGING_LEVEL_ASYNC) == 1);
|
||||||
|
|
||||||
|
struct sigaction sa = {
|
||||||
|
.sa_sigaction = segv_handler,
|
||||||
|
.sa_flags = SA_SIGINFO,
|
||||||
|
};
|
||||||
|
|
||||||
|
assert(sigaction(SIGSEGV, &sa, nullptr) == 0);
|
||||||
|
|
||||||
|
test_fn();
|
||||||
|
do_context_switch();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,95 @@
|
||||||
|
package grapheneos.hmalloc;
|
||||||
|
|
||||||
|
import com.android.tradefed.device.DeviceNotAvailableException;
|
||||||
|
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
|
||||||
|
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
|
@RunWith(DeviceJUnit4ClassRunner.class)
|
||||||
|
public class MemtagTest extends BaseHostJUnit4Test {
|
||||||
|
|
||||||
|
private static final String TEST_BINARY = "/data/local/tmp/memtag_test";
|
||||||
|
|
||||||
|
enum Result {
|
||||||
|
SUCCESS(0, ""),
|
||||||
|
// it's expected that the device is configured to use asymm MTE tag checking mode
|
||||||
|
ASYNC_MTE_ERROR(139, "SEGV_CODE 8"),
|
||||||
|
SYNC_MTE_ERROR(139, "SEGV_CODE 9"),
|
||||||
|
;
|
||||||
|
|
||||||
|
public final int exitCode;
|
||||||
|
public final String stderr;
|
||||||
|
|
||||||
|
Result(int exitCode, String stderr) {
|
||||||
|
this.exitCode = exitCode;
|
||||||
|
this.stderr = stderr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final int SEGV_EXIT_CODE = 139;
|
||||||
|
|
||||||
|
private void runTest(String name, Result expectedResult) throws DeviceNotAvailableException {
|
||||||
|
var args = new ArrayList<String>();
|
||||||
|
args.add(TEST_BINARY);
|
||||||
|
args.add(name);
|
||||||
|
String cmdLine = String.join(" ", args);
|
||||||
|
|
||||||
|
var result = getDevice().executeShellV2Command(cmdLine);
|
||||||
|
|
||||||
|
assertEquals("process exit code", expectedResult.exitCode, result.getExitCode().intValue());
|
||||||
|
assertEquals("stderr", expectedResult.stderr, result.getStderr());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void tag_distinctness() throws DeviceNotAvailableException {
|
||||||
|
runTest("tag_distinctness", Result.SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void read_after_free() throws DeviceNotAvailableException {
|
||||||
|
runTest("read_after_free", Result.SYNC_MTE_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void write_after_free() throws DeviceNotAvailableException {
|
||||||
|
runTest("write_after_free", Result.ASYNC_MTE_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void underflow_read() throws DeviceNotAvailableException {
|
||||||
|
runTest("underflow_read", Result.SYNC_MTE_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void underflow_write() throws DeviceNotAvailableException {
|
||||||
|
runTest("underflow_write", Result.ASYNC_MTE_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void overflow_read() throws DeviceNotAvailableException {
|
||||||
|
runTest("overflow_read", Result.SYNC_MTE_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void overflow_write() throws DeviceNotAvailableException {
|
||||||
|
runTest("overflow_write", Result.ASYNC_MTE_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void untagged_read() throws DeviceNotAvailableException {
|
||||||
|
runTest("untagged_read", Result.SYNC_MTE_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void untagged_write() throws DeviceNotAvailableException {
|
||||||
|
runTest("untagged_write", Result.ASYNC_MTE_ERROR);
|
||||||
|
}
|
||||||
|
}
|
91
src/hardened_malloc/arm_mte.h
Normal file
91
src/hardened_malloc/arm_mte.h
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
#ifndef ARM_MTE_H
|
||||||
|
#define ARM_MTE_H
|
||||||
|
|
||||||
|
#include <arm_acle.h>
|
||||||
|
#include <util.h>
|
||||||
|
|
||||||
|
// Returns a tagged pointer.
|
||||||
|
// See https://developer.arm.com/documentation/ddi0602/2023-09/Base-Instructions/IRG--Insert-Random-Tag-
|
||||||
|
static inline void *arm_mte_create_random_tag(void *p, u64 exclusion_mask) {
|
||||||
|
return __arm_mte_create_random_tag(p, exclusion_mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tag the memory region with the tag specified in tag bits of tagged_ptr. Memory region itself is
|
||||||
|
// zeroed.
|
||||||
|
// tagged_ptr has to be aligned by 16, and len has to be a multiple of 16 (tag granule size).
|
||||||
|
//
|
||||||
|
// Arm's software optimization guide says:
|
||||||
|
// "it is recommended to use STZGM (or DCZGVA) to set tag if data is not a concern." (STZGM and
|
||||||
|
// DCGZVA are zeroing variants of tagging instructions).
|
||||||
|
//
|
||||||
|
// Contents of this function were copied from scudo:
|
||||||
|
// https://android.googlesource.com/platform/external/scudo/+/refs/tags/android-14.0.0_r1/standalone/memtag.h#167
|
||||||
|
//
|
||||||
|
// scudo is licensed under the Apache License v2.0 with LLVM Exceptions, which is compatible with
|
||||||
|
// the hardened_malloc's MIT license
|
||||||
|
static inline void arm_mte_tag_and_clear_mem(void *tagged_ptr, size_t len) {
|
||||||
|
uintptr_t Begin = (uintptr_t) tagged_ptr;
|
||||||
|
uintptr_t End = Begin + len;
|
||||||
|
uintptr_t LineSize, Next, Tmp;
|
||||||
|
__asm__ __volatile__(
|
||||||
|
".arch_extension memtag \n\t"
|
||||||
|
|
||||||
|
// Compute the cache line size in bytes (DCZID_EL0 stores it as the log2
|
||||||
|
// of the number of 4-byte words) and bail out to the slow path if DCZID_EL0
|
||||||
|
// indicates that the DC instructions are unavailable.
|
||||||
|
"DCZID .req %[Tmp] \n\t"
|
||||||
|
"mrs DCZID, dczid_el0 \n\t"
|
||||||
|
"tbnz DCZID, #4, 3f \n\t"
|
||||||
|
"and DCZID, DCZID, #15 \n\t"
|
||||||
|
"mov %[LineSize], #4 \n\t"
|
||||||
|
"lsl %[LineSize], %[LineSize], DCZID \n\t"
|
||||||
|
".unreq DCZID \n\t"
|
||||||
|
|
||||||
|
// Our main loop doesn't handle the case where we don't need to perform any
|
||||||
|
// DC GZVA operations. If the size of our tagged region is less than
|
||||||
|
// twice the cache line size, bail out to the slow path since it's not
|
||||||
|
// guaranteed that we'll be able to do a DC GZVA.
|
||||||
|
"Size .req %[Tmp] \n\t"
|
||||||
|
"sub Size, %[End], %[Cur] \n\t"
|
||||||
|
"cmp Size, %[LineSize], lsl #1 \n\t"
|
||||||
|
"b.lt 3f \n\t"
|
||||||
|
".unreq Size \n\t"
|
||||||
|
|
||||||
|
"LineMask .req %[Tmp] \n\t"
|
||||||
|
"sub LineMask, %[LineSize], #1 \n\t"
|
||||||
|
|
||||||
|
// STZG until the start of the next cache line.
|
||||||
|
"orr %[Next], %[Cur], LineMask \n\t"
|
||||||
|
|
||||||
|
"1:\n\t"
|
||||||
|
"stzg %[Cur], [%[Cur]], #16 \n\t"
|
||||||
|
"cmp %[Cur], %[Next] \n\t"
|
||||||
|
"b.lt 1b \n\t"
|
||||||
|
|
||||||
|
// DC GZVA cache lines until we have no more full cache lines.
|
||||||
|
"bic %[Next], %[End], LineMask \n\t"
|
||||||
|
".unreq LineMask \n\t"
|
||||||
|
|
||||||
|
"2: \n\t"
|
||||||
|
"dc gzva, %[Cur] \n\t"
|
||||||
|
"add %[Cur], %[Cur], %[LineSize] \n\t"
|
||||||
|
"cmp %[Cur], %[Next] \n\t"
|
||||||
|
"b.lt 2b \n\t"
|
||||||
|
|
||||||
|
// STZG until the end of the tagged region. This loop is also used to handle
|
||||||
|
// slow path cases.
|
||||||
|
|
||||||
|
"3: \n\t"
|
||||||
|
"cmp %[Cur], %[End] \n\t"
|
||||||
|
"b.ge 4f \n\t"
|
||||||
|
"stzg %[Cur], [%[Cur]], #16 \n\t"
|
||||||
|
"b 3b \n\t"
|
||||||
|
|
||||||
|
"4: \n\t"
|
||||||
|
|
||||||
|
: [Cur] "+&r"(Begin), [LineSize] "=&r"(LineSize), [Next] "=&r"(Next), [Tmp] "=&r"(Tmp)
|
||||||
|
: [End] "r"(End)
|
||||||
|
: "memory"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
#endif
|
81
src/hardened_malloc/calculate_waste.py
Executable file
81
src/hardened_malloc/calculate_waste.py
Executable file
|
@ -0,0 +1,81 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
from sys import argv
|
||||||
|
|
||||||
|
size_classes = [
|
||||||
|
16, 32, 48, 64, 80, 96, 112, 128,
|
||||||
|
160, 192, 224, 256,
|
||||||
|
320, 384, 448, 512,
|
||||||
|
640, 768, 896, 1024,
|
||||||
|
1280, 1536, 1792, 2048,
|
||||||
|
2560, 3072, 3584, 4096,
|
||||||
|
5120, 6144, 7168, 8192,
|
||||||
|
10240, 12288, 14336, 16384,
|
||||||
|
20480, 24576, 28672, 32768,
|
||||||
|
40960, 49152, 57344, 65536,
|
||||||
|
81920, 98304, 114688, 131072,
|
||||||
|
]
|
||||||
|
|
||||||
|
size_class_slots = [
|
||||||
|
256, 128, 85, 64, 51, 42, 36, 64,
|
||||||
|
51, 64, 54, 64,
|
||||||
|
64, 64, 64, 64,
|
||||||
|
64, 64, 64, 64,
|
||||||
|
16, 16, 16, 16,
|
||||||
|
8, 8, 8, 8,
|
||||||
|
8, 8, 8, 8,
|
||||||
|
6, 5, 4, 4,
|
||||||
|
2, 2, 2, 2,
|
||||||
|
1, 1, 1, 1,
|
||||||
|
1, 1, 1, 1,
|
||||||
|
]
|
||||||
|
|
||||||
|
fragmentation = [100 - 1 / 16 * 100]
|
||||||
|
|
||||||
|
for i in range(len(size_classes) - 1):
|
||||||
|
size_class = size_classes[i + 1]
|
||||||
|
worst_case = size_classes[i] + 1
|
||||||
|
used = worst_case / size_class
|
||||||
|
fragmentation.append(100 - used * 100);
|
||||||
|
|
||||||
|
def page_align(size):
|
||||||
|
return (size + 4095) & ~4095
|
||||||
|
|
||||||
|
print("| ", end="")
|
||||||
|
print("size class", "worst case internal fragmentation", "slab slots", "slab size", "internal fragmentation for slabs", sep=" | ", end=" |\n")
|
||||||
|
print("| ", end='')
|
||||||
|
print("-", "-", "-", "-", "-", sep=" | ", end=" |\n")
|
||||||
|
for size, slots, fragmentation in zip(size_classes, size_class_slots, fragmentation):
|
||||||
|
used = size * slots
|
||||||
|
real = page_align(used)
|
||||||
|
print("| ", end='')
|
||||||
|
print(size, f"{fragmentation:.4}%", slots, real, str(100 - used / real * 100) + "%", sep=" | ", end=" |\n")
|
||||||
|
|
||||||
|
if len(argv) < 2:
|
||||||
|
exit()
|
||||||
|
|
||||||
|
max_bits = 256
|
||||||
|
max_page_span = 16
|
||||||
|
|
||||||
|
print()
|
||||||
|
|
||||||
|
print("maximum bitmap size is {}-bit".format(max_bits))
|
||||||
|
print("maximum page span size is {} ({})".format(max_page_span, max_page_span * 4096))
|
||||||
|
|
||||||
|
for size_class in size_classes:
|
||||||
|
choices = []
|
||||||
|
for bits in range(1, max_bits + 1):
|
||||||
|
used = size_class * bits
|
||||||
|
real = page_align(used)
|
||||||
|
if real > 65536:
|
||||||
|
continue
|
||||||
|
pages = real / 4096
|
||||||
|
efficiency = used / real * 100
|
||||||
|
choices.append((bits, used, real, pages, efficiency))
|
||||||
|
|
||||||
|
choices.sort(key=lambda x: x[4], reverse=True)
|
||||||
|
|
||||||
|
print()
|
||||||
|
print("size_class:", size_class)
|
||||||
|
for choice in choices[:10]:
|
||||||
|
print(choice)
|
177
src/hardened_malloc/chacha.c
Normal file
177
src/hardened_malloc/chacha.c
Normal file
|
@ -0,0 +1,177 @@
|
||||||
|
// Based on chacha-merged.c version 20080118
|
||||||
|
// D. J. Bernstein
|
||||||
|
// Public domain.
|
||||||
|
|
||||||
|
#include "chacha.h"
|
||||||
|
|
||||||
|
// ChaCha8
|
||||||
|
static const unsigned rounds = 8;
|
||||||
|
|
||||||
|
#define U8C(v) (v##U)
|
||||||
|
#define U32C(v) (v##U)
|
||||||
|
|
||||||
|
#define U8V(v) ((u8)(v) & U8C(0xFF))
|
||||||
|
#define U32V(v) ((u32)(v) & U32C(0xFFFFFFFF))
|
||||||
|
|
||||||
|
#define ROTL32(v, n) \
|
||||||
|
(U32V((v) << (n)) | ((v) >> (32 - (n))))
|
||||||
|
|
||||||
|
#define U8TO32_LITTLE(p) \
|
||||||
|
(((u32)((p)[0])) | \
|
||||||
|
((u32)((p)[1]) << 8) | \
|
||||||
|
((u32)((p)[2]) << 16) | \
|
||||||
|
((u32)((p)[3]) << 24))
|
||||||
|
|
||||||
|
#define U32TO8_LITTLE(p, v) \
|
||||||
|
do { \
|
||||||
|
(p)[0] = U8V((v)); \
|
||||||
|
(p)[1] = U8V((v) >> 8); \
|
||||||
|
(p)[2] = U8V((v) >> 16); \
|
||||||
|
(p)[3] = U8V((v) >> 24); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define ROTATE(v, c) (ROTL32(v, c))
|
||||||
|
#define XOR(v, w) ((v) ^ (w))
|
||||||
|
#define PLUS(v, w) (U32V((v) + (w)))
|
||||||
|
#define PLUSONE(v) (PLUS((v), 1))
|
||||||
|
|
||||||
|
#define QUARTERROUND(a, b, c, d) \
|
||||||
|
a = PLUS(a, b); d = ROTATE(XOR(d, a), 16); \
|
||||||
|
c = PLUS(c, d); b = ROTATE(XOR(b, c), 12); \
|
||||||
|
a = PLUS(a, b); d = ROTATE(XOR(d, a), 8); \
|
||||||
|
c = PLUS(c, d); b = ROTATE(XOR(b, c), 7);
|
||||||
|
|
||||||
|
static const char sigma[16] = "expand 32-byte k";
|
||||||
|
|
||||||
|
void chacha_keysetup(chacha_ctx *x, const u8 *k) {
|
||||||
|
x->input[0] = U8TO32_LITTLE(sigma + 0);
|
||||||
|
x->input[1] = U8TO32_LITTLE(sigma + 4);
|
||||||
|
x->input[2] = U8TO32_LITTLE(sigma + 8);
|
||||||
|
x->input[3] = U8TO32_LITTLE(sigma + 12);
|
||||||
|
x->input[4] = U8TO32_LITTLE(k + 0);
|
||||||
|
x->input[5] = U8TO32_LITTLE(k + 4);
|
||||||
|
x->input[6] = U8TO32_LITTLE(k + 8);
|
||||||
|
x->input[7] = U8TO32_LITTLE(k + 12);
|
||||||
|
x->input[8] = U8TO32_LITTLE(k + 16);
|
||||||
|
x->input[9] = U8TO32_LITTLE(k + 20);
|
||||||
|
x->input[10] = U8TO32_LITTLE(k + 24);
|
||||||
|
x->input[11] = U8TO32_LITTLE(k + 28);
|
||||||
|
}
|
||||||
|
|
||||||
|
void chacha_ivsetup(chacha_ctx *x, const u8 *iv) {
|
||||||
|
x->input[12] = 0;
|
||||||
|
x->input[13] = 0;
|
||||||
|
x->input[14] = U8TO32_LITTLE(iv + 0);
|
||||||
|
x->input[15] = U8TO32_LITTLE(iv + 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
void chacha_keystream_bytes(chacha_ctx *x, u8 *c, u32 bytes) {
|
||||||
|
if (!bytes) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
u8 *ctarget;
|
||||||
|
u8 tmp[64];
|
||||||
|
|
||||||
|
u32 j0 = x->input[0];
|
||||||
|
u32 j1 = x->input[1];
|
||||||
|
u32 j2 = x->input[2];
|
||||||
|
u32 j3 = x->input[3];
|
||||||
|
u32 j4 = x->input[4];
|
||||||
|
u32 j5 = x->input[5];
|
||||||
|
u32 j6 = x->input[6];
|
||||||
|
u32 j7 = x->input[7];
|
||||||
|
u32 j8 = x->input[8];
|
||||||
|
u32 j9 = x->input[9];
|
||||||
|
u32 j10 = x->input[10];
|
||||||
|
u32 j11 = x->input[11];
|
||||||
|
u32 j12 = x->input[12];
|
||||||
|
u32 j13 = x->input[13];
|
||||||
|
u32 j14 = x->input[14];
|
||||||
|
u32 j15 = x->input[15];
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
if (bytes < 64) {
|
||||||
|
ctarget = c;
|
||||||
|
c = tmp;
|
||||||
|
}
|
||||||
|
u32 x0 = j0;
|
||||||
|
u32 x1 = j1;
|
||||||
|
u32 x2 = j2;
|
||||||
|
u32 x3 = j3;
|
||||||
|
u32 x4 = j4;
|
||||||
|
u32 x5 = j5;
|
||||||
|
u32 x6 = j6;
|
||||||
|
u32 x7 = j7;
|
||||||
|
u32 x8 = j8;
|
||||||
|
u32 x9 = j9;
|
||||||
|
u32 x10 = j10;
|
||||||
|
u32 x11 = j11;
|
||||||
|
u32 x12 = j12;
|
||||||
|
u32 x13 = j13;
|
||||||
|
u32 x14 = j14;
|
||||||
|
u32 x15 = j15;
|
||||||
|
for (unsigned i = rounds; i > 0; i -= 2) {
|
||||||
|
QUARTERROUND(x0, x4, x8, x12)
|
||||||
|
QUARTERROUND(x1, x5, x9, x13)
|
||||||
|
QUARTERROUND(x2, x6, x10, x14)
|
||||||
|
QUARTERROUND(x3, x7, x11, x15)
|
||||||
|
QUARTERROUND(x0, x5, x10, x15)
|
||||||
|
QUARTERROUND(x1, x6, x11, x12)
|
||||||
|
QUARTERROUND(x2, x7, x8, x13)
|
||||||
|
QUARTERROUND(x3, x4, x9, x14)
|
||||||
|
}
|
||||||
|
x0 = PLUS(x0, j0);
|
||||||
|
x1 = PLUS(x1, j1);
|
||||||
|
x2 = PLUS(x2, j2);
|
||||||
|
x3 = PLUS(x3, j3);
|
||||||
|
x4 = PLUS(x4, j4);
|
||||||
|
x5 = PLUS(x5, j5);
|
||||||
|
x6 = PLUS(x6, j6);
|
||||||
|
x7 = PLUS(x7, j7);
|
||||||
|
x8 = PLUS(x8, j8);
|
||||||
|
x9 = PLUS(x9, j9);
|
||||||
|
x10 = PLUS(x10, j10);
|
||||||
|
x11 = PLUS(x11, j11);
|
||||||
|
x12 = PLUS(x12, j12);
|
||||||
|
x13 = PLUS(x13, j13);
|
||||||
|
x14 = PLUS(x14, j14);
|
||||||
|
x15 = PLUS(x15, j15);
|
||||||
|
|
||||||
|
j12 = PLUSONE(j12);
|
||||||
|
if (!j12) {
|
||||||
|
j13 = PLUSONE(j13);
|
||||||
|
// stopping at 2^70 bytes per nonce is user's responsibility
|
||||||
|
}
|
||||||
|
|
||||||
|
U32TO8_LITTLE(c + 0, x0);
|
||||||
|
U32TO8_LITTLE(c + 4, x1);
|
||||||
|
U32TO8_LITTLE(c + 8, x2);
|
||||||
|
U32TO8_LITTLE(c + 12, x3);
|
||||||
|
U32TO8_LITTLE(c + 16, x4);
|
||||||
|
U32TO8_LITTLE(c + 20, x5);
|
||||||
|
U32TO8_LITTLE(c + 24, x6);
|
||||||
|
U32TO8_LITTLE(c + 28, x7);
|
||||||
|
U32TO8_LITTLE(c + 32, x8);
|
||||||
|
U32TO8_LITTLE(c + 36, x9);
|
||||||
|
U32TO8_LITTLE(c + 40, x10);
|
||||||
|
U32TO8_LITTLE(c + 44, x11);
|
||||||
|
U32TO8_LITTLE(c + 48, x12);
|
||||||
|
U32TO8_LITTLE(c + 52, x13);
|
||||||
|
U32TO8_LITTLE(c + 56, x14);
|
||||||
|
U32TO8_LITTLE(c + 60, x15);
|
||||||
|
|
||||||
|
if (bytes <= 64) {
|
||||||
|
if (bytes < 64) {
|
||||||
|
for (unsigned i = 0; i < bytes; ++i) {
|
||||||
|
ctarget[i] = c[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
x->input[12] = j12;
|
||||||
|
x->input[13] = j13;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
bytes -= 64;
|
||||||
|
c += 64;
|
||||||
|
}
|
||||||
|
}
|
17
src/hardened_malloc/chacha.h
Normal file
17
src/hardened_malloc/chacha.h
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
#ifndef CHACHA_H
|
||||||
|
#define CHACHA_H
|
||||||
|
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
#define CHACHA_KEY_SIZE 32
|
||||||
|
#define CHACHA_IV_SIZE 8
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
u32 input[16];
|
||||||
|
} chacha_ctx;
|
||||||
|
|
||||||
|
void chacha_keysetup(chacha_ctx *x, const u8 *k);
|
||||||
|
void chacha_ivsetup(chacha_ctx *x, const u8 *iv);
|
||||||
|
void chacha_keystream_bytes(chacha_ctx *x, u8 *c, u32 bytes);
|
||||||
|
|
||||||
|
#endif
|
23
src/hardened_malloc/config/default.mk
Normal file
23
src/hardened_malloc/config/default.mk
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
CONFIG_WERROR := true
|
||||||
|
CONFIG_NATIVE := true
|
||||||
|
CONFIG_CXX_ALLOCATOR := true
|
||||||
|
CONFIG_UBSAN := false
|
||||||
|
CONFIG_SEAL_METADATA := false
|
||||||
|
CONFIG_ZERO_ON_FREE := true
|
||||||
|
CONFIG_WRITE_AFTER_FREE_CHECK := true
|
||||||
|
CONFIG_SLOT_RANDOMIZE := true
|
||||||
|
CONFIG_SLAB_CANARY := true
|
||||||
|
CONFIG_SLAB_QUARANTINE_RANDOM_LENGTH := 1
|
||||||
|
CONFIG_SLAB_QUARANTINE_QUEUE_LENGTH := 1
|
||||||
|
CONFIG_EXTENDED_SIZE_CLASSES := true
|
||||||
|
CONFIG_LARGE_SIZE_CLASSES := true
|
||||||
|
CONFIG_GUARD_SLABS_INTERVAL := 1
|
||||||
|
CONFIG_GUARD_SIZE_DIVISOR := 2
|
||||||
|
CONFIG_REGION_QUARANTINE_RANDOM_LENGTH := 256
|
||||||
|
CONFIG_REGION_QUARANTINE_QUEUE_LENGTH := 1024
|
||||||
|
CONFIG_REGION_QUARANTINE_SKIP_THRESHOLD := 33554432 # 32MiB
|
||||||
|
CONFIG_FREE_SLABS_QUARANTINE_RANDOM_LENGTH := 32
|
||||||
|
CONFIG_CLASS_REGION_SIZE := 34359738368 # 32GiB
|
||||||
|
CONFIG_N_ARENA := 4
|
||||||
|
CONFIG_STATS := false
|
||||||
|
CONFIG_SELF_INIT := true
|
23
src/hardened_malloc/config/light.mk
Normal file
23
src/hardened_malloc/config/light.mk
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
CONFIG_WERROR := true
|
||||||
|
CONFIG_NATIVE := true
|
||||||
|
CONFIG_CXX_ALLOCATOR := true
|
||||||
|
CONFIG_UBSAN := false
|
||||||
|
CONFIG_SEAL_METADATA := false
|
||||||
|
CONFIG_ZERO_ON_FREE := true
|
||||||
|
CONFIG_WRITE_AFTER_FREE_CHECK := false
|
||||||
|
CONFIG_SLOT_RANDOMIZE := false
|
||||||
|
CONFIG_SLAB_CANARY := true
|
||||||
|
CONFIG_SLAB_QUARANTINE_RANDOM_LENGTH := 0
|
||||||
|
CONFIG_SLAB_QUARANTINE_QUEUE_LENGTH := 0
|
||||||
|
CONFIG_EXTENDED_SIZE_CLASSES := true
|
||||||
|
CONFIG_LARGE_SIZE_CLASSES := true
|
||||||
|
CONFIG_GUARD_SLABS_INTERVAL := 8
|
||||||
|
CONFIG_GUARD_SIZE_DIVISOR := 2
|
||||||
|
CONFIG_REGION_QUARANTINE_RANDOM_LENGTH := 256
|
||||||
|
CONFIG_REGION_QUARANTINE_QUEUE_LENGTH := 1024
|
||||||
|
CONFIG_REGION_QUARANTINE_SKIP_THRESHOLD := 33554432 # 32MiB
|
||||||
|
CONFIG_FREE_SLABS_QUARANTINE_RANDOM_LENGTH := 32
|
||||||
|
CONFIG_CLASS_REGION_SIZE := 34359738368 # 32GiB
|
||||||
|
CONFIG_N_ARENA := 4
|
||||||
|
CONFIG_STATS := false
|
||||||
|
CONFIG_SELF_INIT := true
|
2190
src/hardened_malloc/h_malloc.c
Normal file
2190
src/hardened_malloc/h_malloc.c
Normal file
File diff suppressed because it is too large
Load diff
129
src/hardened_malloc/include/h_malloc.h
Normal file
129
src/hardened_malloc/include/h_malloc.h
Normal file
|
@ -0,0 +1,129 @@
|
||||||
|
#ifndef ALLOCATOR_H
|
||||||
|
#define ALLOCATOR_H
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include <malloc.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef H_MALLOC_PREFIX
|
||||||
|
#define h_malloc malloc
|
||||||
|
#define h_calloc calloc
|
||||||
|
#define h_realloc realloc
|
||||||
|
#define h_aligned_alloc aligned_alloc
|
||||||
|
#define h_free free
|
||||||
|
|
||||||
|
#define h_posix_memalign posix_memalign
|
||||||
|
|
||||||
|
#define h_malloc_usable_size malloc_usable_size
|
||||||
|
#define h_mallopt mallopt
|
||||||
|
#define h_malloc_trim malloc_trim
|
||||||
|
#define h_malloc_stats malloc_stats
|
||||||
|
#define h_mallinfo mallinfo
|
||||||
|
#define h_mallinfo2 mallinfo2
|
||||||
|
#define h_malloc_info malloc_info
|
||||||
|
|
||||||
|
#define h_memalign memalign
|
||||||
|
#define h_valloc valloc
|
||||||
|
#define h_pvalloc pvalloc
|
||||||
|
#define h_cfree cfree
|
||||||
|
#define h_malloc_get_state malloc_get_state
|
||||||
|
#define h_malloc_set_state malloc_set_state
|
||||||
|
|
||||||
|
#define h_mallinfo_narenas mallinfo_narenas
|
||||||
|
#define h_mallinfo_nbins mallinfo_nbins
|
||||||
|
#define h_mallinfo_arena_info mallinfo_arena_info
|
||||||
|
#define h_mallinfo_bin_info mallinfo_bin_info
|
||||||
|
|
||||||
|
#define h_malloc_iterate malloc_iterate
|
||||||
|
#define h_malloc_disable malloc_disable
|
||||||
|
#define h_malloc_enable malloc_enable
|
||||||
|
|
||||||
|
#define h_malloc_object_size malloc_object_size
|
||||||
|
#define h_malloc_object_size_fast malloc_object_size_fast
|
||||||
|
#define h_free_sized free_sized
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// C standard
|
||||||
|
__attribute__((malloc)) __attribute__((alloc_size(1))) void *h_malloc(size_t size);
|
||||||
|
__attribute__((malloc)) __attribute__((alloc_size(1, 2))) void *h_calloc(size_t nmemb, size_t size);
|
||||||
|
__attribute__((alloc_size(2))) void *h_realloc(void *ptr, size_t size);
|
||||||
|
__attribute__((malloc)) __attribute__((alloc_size(2))) __attribute__((alloc_align(1)))
|
||||||
|
void *h_aligned_alloc(size_t alignment, size_t size);
|
||||||
|
void h_free(void *ptr);
|
||||||
|
|
||||||
|
// POSIX
|
||||||
|
int h_posix_memalign(void **memptr, size_t alignment, size_t size);
|
||||||
|
|
||||||
|
#ifdef __ANDROID__
|
||||||
|
#define H_MALLOC_USABLE_SIZE_CONST const
|
||||||
|
#else
|
||||||
|
#define H_MALLOC_USABLE_SIZE_CONST
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// glibc extensions
|
||||||
|
size_t h_malloc_usable_size(H_MALLOC_USABLE_SIZE_CONST void *ptr);
|
||||||
|
int h_mallopt(int param, int value);
|
||||||
|
int h_malloc_trim(size_t pad);
|
||||||
|
void h_malloc_stats(void);
|
||||||
|
#if defined(__GLIBC__) || defined(__ANDROID__)
|
||||||
|
struct mallinfo h_mallinfo(void);
|
||||||
|
#endif
|
||||||
|
#ifndef __ANDROID__
|
||||||
|
int h_malloc_info(int options, FILE *fp);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// obsolete glibc extensions
|
||||||
|
__attribute__((malloc)) __attribute__((alloc_size(2))) __attribute__((alloc_align(1)))
|
||||||
|
void *h_memalign(size_t alignment, size_t size);
|
||||||
|
#ifndef __ANDROID__
|
||||||
|
__attribute__((malloc)) __attribute__((alloc_size(1))) void *h_valloc(size_t size);
|
||||||
|
__attribute__((malloc)) void *h_pvalloc(size_t size);
|
||||||
|
#endif
|
||||||
|
#ifdef __GLIBC__
|
||||||
|
void h_cfree(void *ptr) __THROW;
|
||||||
|
void *h_malloc_get_state(void);
|
||||||
|
int h_malloc_set_state(void *state);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Android extensions
|
||||||
|
#ifdef __ANDROID__
|
||||||
|
size_t h_mallinfo_narenas(void);
|
||||||
|
size_t h_mallinfo_nbins(void);
|
||||||
|
struct mallinfo h_mallinfo_arena_info(size_t arena);
|
||||||
|
struct mallinfo h_mallinfo_bin_info(size_t arena, size_t bin);
|
||||||
|
int h_malloc_iterate(uintptr_t base, size_t size, void (*callback)(uintptr_t ptr, size_t size, void *arg),
|
||||||
|
void *arg);
|
||||||
|
void h_malloc_disable(void);
|
||||||
|
void h_malloc_enable(void);
|
||||||
|
void h_malloc_disable_memory_tagging(void);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// hardened_malloc extensions
|
||||||
|
|
||||||
|
// return an upper bound on object size for any pointer based on malloc metadata
|
||||||
|
size_t h_malloc_object_size(const void *ptr);
|
||||||
|
|
||||||
|
// similar to malloc_object_size, but avoiding locking so the results are much more limited
|
||||||
|
size_t h_malloc_object_size_fast(const void *ptr);
|
||||||
|
|
||||||
|
// The free function with an extra parameter for passing the size requested at
|
||||||
|
// allocation time.
|
||||||
|
//
|
||||||
|
// This offers the same functionality as C++14 sized deallocation and can be
|
||||||
|
// used to implement it.
|
||||||
|
//
|
||||||
|
// A performance-oriented allocator would use this as a performance
|
||||||
|
// enhancement with undefined behavior on a mismatch. Instead, this hardened
|
||||||
|
// allocator implementation uses it to improve security by checking that the
|
||||||
|
// passed size matches the allocated size.
|
||||||
|
void h_free_sized(void *ptr, size_t expected_size);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
120
src/hardened_malloc/memory.c
Normal file
120
src/hardened_malloc/memory.c
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#include <sys/mman.h>
|
||||||
|
|
||||||
|
#ifdef LABEL_MEMORY
|
||||||
|
#include <sys/prctl.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef PR_SET_VMA
|
||||||
|
#define PR_SET_VMA 0x53564d41
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef PR_SET_VMA_ANON_NAME
|
||||||
|
#define PR_SET_VMA_ANON_NAME 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "memory.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
void *memory_map(size_t size) {
|
||||||
|
void *p = mmap(NULL, size, PROT_NONE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
|
||||||
|
if (unlikely(p == MAP_FAILED)) {
|
||||||
|
if (errno != ENOMEM) {
|
||||||
|
fatal_error("non-ENOMEM mmap failure");
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef HAS_ARM_MTE
|
||||||
|
// Note that PROT_MTE can't be cleared via mprotect
|
||||||
|
void *memory_map_mte(size_t size) {
|
||||||
|
void *p = mmap(NULL, size, PROT_MTE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
|
||||||
|
if (unlikely(p == MAP_FAILED)) {
|
||||||
|
if (errno != ENOMEM) {
|
||||||
|
fatal_error("non-ENOMEM MTE mmap failure");
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
bool memory_map_fixed(void *ptr, size_t size) {
|
||||||
|
void *p = mmap(ptr, size, PROT_NONE, MAP_ANONYMOUS|MAP_PRIVATE|MAP_FIXED, -1, 0);
|
||||||
|
bool ret = p == MAP_FAILED;
|
||||||
|
if (unlikely(ret) && errno != ENOMEM) {
|
||||||
|
fatal_error("non-ENOMEM MAP_FIXED mmap failure");
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool memory_unmap(void *ptr, size_t size) {
|
||||||
|
bool ret = munmap(ptr, size);
|
||||||
|
if (unlikely(ret) && errno != ENOMEM) {
|
||||||
|
fatal_error("non-ENOMEM munmap failure");
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool memory_protect_prot(void *ptr, size_t size, int prot, UNUSED int pkey) {
|
||||||
|
#ifdef USE_PKEY
|
||||||
|
bool ret = pkey_mprotect(ptr, size, prot, pkey);
|
||||||
|
#else
|
||||||
|
bool ret = mprotect(ptr, size, prot);
|
||||||
|
#endif
|
||||||
|
if (unlikely(ret) && errno != ENOMEM) {
|
||||||
|
fatal_error("non-ENOMEM mprotect failure");
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool memory_protect_ro(void *ptr, size_t size) {
|
||||||
|
return memory_protect_prot(ptr, size, PROT_READ, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool memory_protect_rw(void *ptr, size_t size) {
|
||||||
|
return memory_protect_prot(ptr, size, PROT_READ|PROT_WRITE, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool memory_protect_rw_metadata(void *ptr, size_t size) {
|
||||||
|
return memory_protect_prot(ptr, size, PROT_READ|PROT_WRITE, get_metadata_key());
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef HAVE_COMPATIBLE_MREMAP
|
||||||
|
bool memory_remap(void *old, size_t old_size, size_t new_size) {
|
||||||
|
void *ptr = mremap(old, old_size, new_size, 0);
|
||||||
|
bool ret = ptr == MAP_FAILED;
|
||||||
|
if (unlikely(ret) && errno != ENOMEM) {
|
||||||
|
fatal_error("non-ENOMEM mremap failure");
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool memory_remap_fixed(void *old, size_t old_size, void *new, size_t new_size) {
|
||||||
|
void *ptr = mremap(old, old_size, new_size, MREMAP_MAYMOVE|MREMAP_FIXED, new);
|
||||||
|
bool ret = ptr == MAP_FAILED;
|
||||||
|
if (unlikely(ret) && errno != ENOMEM) {
|
||||||
|
fatal_error("non-ENOMEM MREMAP_FIXED mremap failure");
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
bool memory_purge(void *ptr, size_t size) {
|
||||||
|
int ret = madvise(ptr, size, MADV_DONTNEED);
|
||||||
|
if (unlikely(ret) && errno != ENOMEM) {
|
||||||
|
fatal_error("non-ENOMEM MADV_DONTNEED madvise failure");
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool memory_set_name(UNUSED void *ptr, UNUSED size_t size, UNUSED const char *name) {
|
||||||
|
#ifdef LABEL_MEMORY
|
||||||
|
return prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, ptr, size, name);
|
||||||
|
#else
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
29
src/hardened_malloc/memory.h
Normal file
29
src/hardened_malloc/memory.h
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
#ifndef MEMORY_H
|
||||||
|
#define MEMORY_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#ifdef __linux__
|
||||||
|
#define HAVE_COMPATIBLE_MREMAP
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int get_metadata_key(void);
|
||||||
|
|
||||||
|
void *memory_map(size_t size);
|
||||||
|
#ifdef HAS_ARM_MTE
|
||||||
|
void *memory_map_mte(size_t size);
|
||||||
|
#endif
|
||||||
|
bool memory_map_fixed(void *ptr, size_t size);
|
||||||
|
bool memory_unmap(void *ptr, size_t size);
|
||||||
|
bool memory_protect_ro(void *ptr, size_t size);
|
||||||
|
bool memory_protect_rw(void *ptr, size_t size);
|
||||||
|
bool memory_protect_rw_metadata(void *ptr, size_t size);
|
||||||
|
#ifdef HAVE_COMPATIBLE_MREMAP
|
||||||
|
bool memory_remap(void *old, size_t old_size, size_t new_size);
|
||||||
|
bool memory_remap_fixed(void *old, size_t old_size, void *new, size_t new_size);
|
||||||
|
#endif
|
||||||
|
bool memory_purge(void *ptr, size_t size);
|
||||||
|
bool memory_set_name(void *ptr, size_t size, const char *name);
|
||||||
|
|
||||||
|
#endif
|
49
src/hardened_malloc/memtag.h
Normal file
49
src/hardened_malloc/memtag.h
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
#ifndef MEMTAG_H
|
||||||
|
#define MEMTAG_H
|
||||||
|
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
#ifdef HAS_ARM_MTE
|
||||||
|
#include "arm_mte.h"
|
||||||
|
#define MEMTAG 1
|
||||||
|
#define RESERVED_TAG 15
|
||||||
|
#define TAG_WIDTH 4
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static inline void *untag_pointer(void *ptr) {
|
||||||
|
#ifdef HAS_ARM_MTE
|
||||||
|
const uintptr_t mask = UINTPTR_MAX >> 8;
|
||||||
|
return (void *) ((uintptr_t) ptr & mask);
|
||||||
|
#else
|
||||||
|
return ptr;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline const void *untag_const_pointer(const void *ptr) {
|
||||||
|
#ifdef HAS_ARM_MTE
|
||||||
|
const uintptr_t mask = UINTPTR_MAX >> 8;
|
||||||
|
return (const void *) ((uintptr_t) ptr & mask);
|
||||||
|
#else
|
||||||
|
return ptr;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void *set_pointer_tag(void *ptr, u8 tag) {
|
||||||
|
#ifdef HAS_ARM_MTE
|
||||||
|
return (void *) (((uintptr_t) tag << 56) | (uintptr_t) untag_pointer(ptr));
|
||||||
|
#else
|
||||||
|
(void) tag;
|
||||||
|
return ptr;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u8 get_pointer_tag(void *ptr) {
|
||||||
|
#ifdef HAS_ARM_MTE
|
||||||
|
return (((uintptr_t) ptr) >> 56) & 0xf;
|
||||||
|
#else
|
||||||
|
(void) ptr;
|
||||||
|
return 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
28
src/hardened_malloc/mutex.h
Normal file
28
src/hardened_malloc/mutex.h
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
#ifndef MUTEX_H
|
||||||
|
#define MUTEX_H
|
||||||
|
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
struct mutex {
|
||||||
|
pthread_mutex_t lock;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define MUTEX_INITIALIZER (struct mutex){PTHREAD_MUTEX_INITIALIZER}
|
||||||
|
|
||||||
|
static inline void mutex_init(struct mutex *m) {
|
||||||
|
if (unlikely(pthread_mutex_init(&m->lock, NULL))) {
|
||||||
|
fatal_error("mutex initialization failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void mutex_lock(struct mutex *m) {
|
||||||
|
pthread_mutex_lock(&m->lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void mutex_unlock(struct mutex *m) {
|
||||||
|
pthread_mutex_unlock(&m->lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
153
src/hardened_malloc/new.cc
Normal file
153
src/hardened_malloc/new.cc
Normal file
|
@ -0,0 +1,153 @@
|
||||||
|
// needed with libstdc++ but not libc++
|
||||||
|
#if __has_include(<bits/functexcept.h>)
|
||||||
|
#include <bits/functexcept.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <new>
|
||||||
|
|
||||||
|
#include "h_malloc.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
COLD static void *handle_out_of_memory(size_t size, bool nothrow) {
|
||||||
|
void *ptr = nullptr;
|
||||||
|
|
||||||
|
do {
|
||||||
|
std::new_handler handler = std::get_new_handler();
|
||||||
|
if (handler == nullptr) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
handler();
|
||||||
|
} catch (const std::bad_alloc &) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ptr = h_malloc(size);
|
||||||
|
} while (ptr == nullptr);
|
||||||
|
|
||||||
|
if (ptr == nullptr && !nothrow) {
|
||||||
|
std::__throw_bad_alloc();
|
||||||
|
}
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void *new_impl(size_t size, bool nothrow) {
|
||||||
|
void *ptr = h_malloc(size);
|
||||||
|
if (likely(ptr != nullptr)) {
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
return handle_out_of_memory(size, nothrow);
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPORT void *operator new(size_t size) {
|
||||||
|
return new_impl(size, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPORT void *operator new[](size_t size) {
|
||||||
|
return new_impl(size, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPORT void *operator new(size_t size, const std::nothrow_t &) noexcept {
|
||||||
|
return new_impl(size, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPORT void *operator new[](size_t size, const std::nothrow_t &) noexcept {
|
||||||
|
return new_impl(size, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPORT void operator delete(void *ptr) noexcept {
|
||||||
|
h_free(ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPORT void operator delete[](void *ptr) noexcept {
|
||||||
|
h_free(ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPORT void operator delete(void *ptr, const std::nothrow_t &) noexcept {
|
||||||
|
h_free(ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPORT void operator delete[](void *ptr, const std::nothrow_t &) noexcept {
|
||||||
|
h_free(ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPORT void operator delete(void *ptr, size_t size) noexcept {
|
||||||
|
h_free_sized(ptr, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPORT void operator delete[](void *ptr, size_t size) noexcept {
|
||||||
|
h_free_sized(ptr, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
COLD static void *handle_out_of_memory(size_t size, size_t alignment, bool nothrow) {
|
||||||
|
void *ptr = nullptr;
|
||||||
|
|
||||||
|
do {
|
||||||
|
std::new_handler handler = std::get_new_handler();
|
||||||
|
if (handler == nullptr) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
handler();
|
||||||
|
} catch (const std::bad_alloc &) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ptr = h_aligned_alloc(alignment, size);
|
||||||
|
} while (ptr == nullptr);
|
||||||
|
|
||||||
|
if (ptr == nullptr && !nothrow) {
|
||||||
|
std::__throw_bad_alloc();
|
||||||
|
}
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void *new_impl(size_t size, size_t alignment, bool nothrow) {
|
||||||
|
void *ptr = h_aligned_alloc(alignment, size);
|
||||||
|
if (likely(ptr != nullptr)) {
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
return handle_out_of_memory(size, alignment, nothrow);
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPORT void *operator new(size_t size, std::align_val_t alignment) {
|
||||||
|
return new_impl(size, static_cast<size_t>(alignment), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPORT void *operator new[](size_t size, std::align_val_t alignment) {
|
||||||
|
return new_impl(size, static_cast<size_t>(alignment), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPORT void *operator new(size_t size, std::align_val_t alignment, const std::nothrow_t &) noexcept {
|
||||||
|
return new_impl(size, static_cast<size_t>(alignment), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPORT void *operator new[](size_t size, std::align_val_t alignment, const std::nothrow_t &) noexcept {
|
||||||
|
return new_impl(size, static_cast<size_t>(alignment), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPORT void operator delete(void *ptr, std::align_val_t) noexcept {
|
||||||
|
h_free(ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPORT void operator delete[](void *ptr, std::align_val_t) noexcept {
|
||||||
|
h_free(ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPORT void operator delete(void *ptr, std::align_val_t, const std::nothrow_t &) noexcept {
|
||||||
|
h_free(ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPORT void operator delete[](void *ptr, std::align_val_t, const std::nothrow_t &) noexcept {
|
||||||
|
h_free(ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPORT void operator delete(void *ptr, size_t size, std::align_val_t) noexcept {
|
||||||
|
h_free_sized(ptr, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPORT void operator delete[](void *ptr, size_t size, std::align_val_t) noexcept {
|
||||||
|
h_free_sized(ptr, size);
|
||||||
|
}
|
88
src/hardened_malloc/pages.c
Normal file
88
src/hardened_malloc/pages.c
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#include "memory.h"
|
||||||
|
#include "pages.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
static bool add_guards(size_t size, size_t guard_size, size_t *total_size) {
|
||||||
|
return __builtin_add_overflow(size, guard_size, total_size) ||
|
||||||
|
__builtin_add_overflow(*total_size, guard_size, total_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void *allocate_pages(size_t usable_size, size_t guard_size, bool unprotect, const char *name) {
|
||||||
|
size_t real_size;
|
||||||
|
if (unlikely(add_guards(usable_size, guard_size, &real_size))) {
|
||||||
|
errno = ENOMEM;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
void *real = memory_map(real_size);
|
||||||
|
if (unlikely(real == NULL)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
memory_set_name(real, real_size, name);
|
||||||
|
void *usable = (char *)real + guard_size;
|
||||||
|
if (unprotect && unlikely(memory_protect_rw(usable, usable_size))) {
|
||||||
|
memory_unmap(real, real_size);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return usable;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *allocate_pages_aligned(size_t usable_size, size_t alignment, size_t guard_size, const char *name) {
|
||||||
|
usable_size = page_align(usable_size);
|
||||||
|
if (unlikely(!usable_size)) {
|
||||||
|
errno = ENOMEM;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t alloc_size;
|
||||||
|
if (unlikely(__builtin_add_overflow(usable_size, alignment - PAGE_SIZE, &alloc_size))) {
|
||||||
|
errno = ENOMEM;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t real_alloc_size;
|
||||||
|
if (unlikely(add_guards(alloc_size, guard_size, &real_alloc_size))) {
|
||||||
|
errno = ENOMEM;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *real = memory_map(real_alloc_size);
|
||||||
|
if (unlikely(real == NULL)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
memory_set_name(real, real_alloc_size, name);
|
||||||
|
|
||||||
|
void *usable = (char *)real + guard_size;
|
||||||
|
|
||||||
|
size_t lead_size = align((uintptr_t)usable, alignment) - (uintptr_t)usable;
|
||||||
|
size_t trail_size = alloc_size - lead_size - usable_size;
|
||||||
|
void *base = (char *)usable + lead_size;
|
||||||
|
|
||||||
|
if (unlikely(memory_protect_rw(base, usable_size))) {
|
||||||
|
memory_unmap(real, real_alloc_size);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lead_size) {
|
||||||
|
if (unlikely(memory_unmap(real, lead_size))) {
|
||||||
|
memory_unmap(real, real_alloc_size);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (trail_size) {
|
||||||
|
if (unlikely(memory_unmap((char *)base + usable_size + guard_size, trail_size))) {
|
||||||
|
memory_unmap(real, real_alloc_size);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return base;
|
||||||
|
}
|
||||||
|
|
||||||
|
void deallocate_pages(void *usable, size_t usable_size, size_t guard_size) {
|
||||||
|
if (unlikely(memory_unmap((char *)usable - guard_size, usable_size + guard_size * 2))) {
|
||||||
|
memory_purge(usable, usable_size);
|
||||||
|
}
|
||||||
|
}
|
32
src/hardened_malloc/pages.h
Normal file
32
src/hardened_malloc/pages.h
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
#ifndef PAGES_H
|
||||||
|
#define PAGES_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
#define PAGE_SHIFT 12
|
||||||
|
#ifndef PAGE_SIZE
|
||||||
|
#define PAGE_SIZE ((size_t)1 << PAGE_SHIFT)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void *allocate_pages(size_t usable_size, size_t guard_size, bool unprotect, const char *name);
|
||||||
|
void *allocate_pages_aligned(size_t usable_size, size_t alignment, size_t guard_size, const char *name);
|
||||||
|
void deallocate_pages(void *usable, size_t usable_size, size_t guard_size);
|
||||||
|
|
||||||
|
static inline size_t page_align(size_t size) {
|
||||||
|
return align(size, PAGE_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline size_t hash_page(const void *p) {
|
||||||
|
uintptr_t u = (uintptr_t)p >> PAGE_SHIFT;
|
||||||
|
size_t sum = u;
|
||||||
|
sum = (sum << 7) - sum + (u >> 16);
|
||||||
|
sum = (sum << 7) - sum + (u >> 32);
|
||||||
|
sum = (sum << 7) - sum + (u >> 48);
|
||||||
|
return sum;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
6
src/hardened_malloc/preload.sh
Executable file
6
src/hardened_malloc/preload.sh
Executable file
|
@ -0,0 +1,6 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
[[ $LD_PRELOAD ]] && LD_PRELOAD+=" "
|
||||||
|
export LD_PRELOAD+="$dir/libhardened_malloc.so"
|
||||||
|
exec "$@"
|
128
src/hardened_malloc/random.c
Normal file
128
src/hardened_malloc/random.c
Normal file
|
@ -0,0 +1,128 @@
|
||||||
|
#include <errno.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "chacha.h"
|
||||||
|
#include "random.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
#include <sys/random.h>
|
||||||
|
|
||||||
|
static void get_random_seed(void *buf, size_t size) {
|
||||||
|
while (size) {
|
||||||
|
ssize_t r;
|
||||||
|
|
||||||
|
do {
|
||||||
|
r = getrandom(buf, size, 0);
|
||||||
|
} while (r == -1 && errno == EINTR);
|
||||||
|
|
||||||
|
if (r <= 0) {
|
||||||
|
fatal_error("getrandom failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
buf = (char *)buf + r;
|
||||||
|
size -= r;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void random_state_init(struct random_state *state) {
|
||||||
|
u8 rnd[CHACHA_KEY_SIZE + CHACHA_IV_SIZE];
|
||||||
|
get_random_seed(rnd, sizeof(rnd));
|
||||||
|
chacha_keysetup(&state->ctx, rnd);
|
||||||
|
chacha_ivsetup(&state->ctx, rnd + CHACHA_KEY_SIZE);
|
||||||
|
state->index = RANDOM_CACHE_SIZE;
|
||||||
|
state->reseed = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void random_state_init_from_random_state(struct random_state *state, struct random_state *source) {
|
||||||
|
u8 rnd[CHACHA_KEY_SIZE + CHACHA_IV_SIZE];
|
||||||
|
get_random_bytes(source, rnd, sizeof(rnd));
|
||||||
|
chacha_keysetup(&state->ctx, rnd);
|
||||||
|
chacha_ivsetup(&state->ctx, rnd + CHACHA_KEY_SIZE);
|
||||||
|
state->index = RANDOM_CACHE_SIZE;
|
||||||
|
state->reseed = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void refill(struct random_state *state) {
|
||||||
|
if (state->reseed >= RANDOM_RESEED_SIZE) {
|
||||||
|
random_state_init(state);
|
||||||
|
}
|
||||||
|
chacha_keystream_bytes(&state->ctx, state->cache, RANDOM_CACHE_SIZE);
|
||||||
|
state->index = 0;
|
||||||
|
state->reseed += RANDOM_CACHE_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void get_random_bytes(struct random_state *state, void *buf, size_t size) {
|
||||||
|
// avoid needless copying to and from the cache as an optimization
|
||||||
|
if (size > RANDOM_CACHE_SIZE / 2) {
|
||||||
|
chacha_keystream_bytes(&state->ctx, buf, size);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (size) {
|
||||||
|
if (state->index == RANDOM_CACHE_SIZE) {
|
||||||
|
refill(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t remaining = RANDOM_CACHE_SIZE - state->index;
|
||||||
|
size_t copy_size = min(size, remaining);
|
||||||
|
memcpy(buf, state->cache + state->index, copy_size);
|
||||||
|
state->index += copy_size;
|
||||||
|
|
||||||
|
buf = (char *)buf + copy_size;
|
||||||
|
size -= copy_size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
u16 get_random_u16(struct random_state *state) {
|
||||||
|
u16 value;
|
||||||
|
unsigned remaining = RANDOM_CACHE_SIZE - state->index;
|
||||||
|
if (remaining < sizeof(value)) {
|
||||||
|
refill(state);
|
||||||
|
}
|
||||||
|
memcpy(&value, state->cache + state->index, sizeof(value));
|
||||||
|
state->index += sizeof(value);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// See Fast Random Integer Generation in an Interval by Daniel Lemire
|
||||||
|
u16 get_random_u16_uniform(struct random_state *state, u16 bound) {
|
||||||
|
u32 random = get_random_u16(state);
|
||||||
|
u32 multiresult = random * bound;
|
||||||
|
u16 leftover = multiresult;
|
||||||
|
if (leftover < bound) {
|
||||||
|
u16 threshold = -bound % bound;
|
||||||
|
while (leftover < threshold) {
|
||||||
|
random = get_random_u16(state);
|
||||||
|
multiresult = random * bound;
|
||||||
|
leftover = (u16)multiresult;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return multiresult >> 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 get_random_u64(struct random_state *state) {
|
||||||
|
u64 value;
|
||||||
|
unsigned remaining = RANDOM_CACHE_SIZE - state->index;
|
||||||
|
if (remaining < sizeof(value)) {
|
||||||
|
refill(state);
|
||||||
|
}
|
||||||
|
memcpy(&value, state->cache + state->index, sizeof(value));
|
||||||
|
state->index += sizeof(value);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// See Fast Random Integer Generation in an Interval by Daniel Lemire
|
||||||
|
u64 get_random_u64_uniform(struct random_state *state, u64 bound) {
|
||||||
|
u128 random = get_random_u64(state);
|
||||||
|
u128 multiresult = random * bound;
|
||||||
|
u64 leftover = multiresult;
|
||||||
|
if (leftover < bound) {
|
||||||
|
u64 threshold = -bound % bound;
|
||||||
|
while (leftover < threshold) {
|
||||||
|
random = get_random_u64(state);
|
||||||
|
multiresult = random * bound;
|
||||||
|
leftover = multiresult;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return multiresult >> 64;
|
||||||
|
}
|
25
src/hardened_malloc/random.h
Normal file
25
src/hardened_malloc/random.h
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
#ifndef RANDOM_H
|
||||||
|
#define RANDOM_H
|
||||||
|
|
||||||
|
#include "chacha.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
#define RANDOM_CACHE_SIZE 256U
|
||||||
|
#define RANDOM_RESEED_SIZE (256U * 1024)
|
||||||
|
|
||||||
|
struct random_state {
|
||||||
|
unsigned index;
|
||||||
|
unsigned reseed;
|
||||||
|
chacha_ctx ctx;
|
||||||
|
u8 cache[RANDOM_CACHE_SIZE];
|
||||||
|
};
|
||||||
|
|
||||||
|
void random_state_init(struct random_state *state);
|
||||||
|
void random_state_init_from_random_state(struct random_state *state, struct random_state *source);
|
||||||
|
void get_random_bytes(struct random_state *state, void *buf, size_t size);
|
||||||
|
u16 get_random_u16(struct random_state *state);
|
||||||
|
u16 get_random_u16_uniform(struct random_state *state, u16 bound);
|
||||||
|
u64 get_random_u64(struct random_state *state);
|
||||||
|
u64 get_random_u64_uniform(struct random_state *state, u64 bound);
|
||||||
|
|
||||||
|
#endif
|
44
src/hardened_malloc/test/.gitignore
vendored
Normal file
44
src/hardened_malloc/test/.gitignore
vendored
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
large_array_growth
|
||||||
|
mallinfo
|
||||||
|
mallinfo2
|
||||||
|
malloc_info
|
||||||
|
offset
|
||||||
|
delete_type_size_mismatch
|
||||||
|
double_free_large
|
||||||
|
double_free_large_delayed
|
||||||
|
double_free_small
|
||||||
|
double_free_small_delayed
|
||||||
|
invalid_free_protected
|
||||||
|
invalid_free_small_region
|
||||||
|
invalid_free_small_region_far
|
||||||
|
invalid_free_unprotected
|
||||||
|
read_after_free_large
|
||||||
|
read_after_free_small
|
||||||
|
read_zero_size
|
||||||
|
string_overflow
|
||||||
|
unaligned_free_large
|
||||||
|
unaligned_free_small
|
||||||
|
uninitialized_free
|
||||||
|
uninitialized_malloc_usable_size
|
||||||
|
uninitialized_realloc
|
||||||
|
write_after_free_large
|
||||||
|
write_after_free_large_reuse
|
||||||
|
write_after_free_small
|
||||||
|
write_after_free_small_reuse
|
||||||
|
write_zero_size
|
||||||
|
unaligned_malloc_usable_size_small
|
||||||
|
invalid_malloc_usable_size_small
|
||||||
|
invalid_malloc_usable_size_small_quarantine
|
||||||
|
malloc_object_size
|
||||||
|
malloc_object_size_offset
|
||||||
|
invalid_malloc_object_size_small
|
||||||
|
invalid_malloc_object_size_small_quarantine
|
||||||
|
impossibly_large_malloc
|
||||||
|
overflow_large_1_byte
|
||||||
|
overflow_large_8_byte
|
||||||
|
overflow_small_1_byte
|
||||||
|
overflow_small_8_byte
|
||||||
|
uninitialized_read_large
|
||||||
|
uninitialized_read_small
|
||||||
|
realloc_init
|
||||||
|
__pycache__/
|
76
src/hardened_malloc/test/Makefile
Normal file
76
src/hardened_malloc/test/Makefile
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
CONFIG_SLAB_CANARY := true
|
||||||
|
CONFIG_EXTENDED_SIZE_CLASSES := true
|
||||||
|
|
||||||
|
ifneq ($(VARIANT),)
|
||||||
|
$(error testing non-default variants not yet supported)
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq (,$(filter $(CONFIG_SLAB_CANARY),true false))
|
||||||
|
$(error CONFIG_SLAB_CANARY must be true or false)
|
||||||
|
endif
|
||||||
|
|
||||||
|
dir=$(dir $(realpath $(firstword $(MAKEFILE_LIST))))
|
||||||
|
|
||||||
|
CPPFLAGS := \
|
||||||
|
-D_GNU_SOURCE \
|
||||||
|
-DSLAB_CANARY=$(CONFIG_SLAB_CANARY) \
|
||||||
|
-DCONFIG_EXTENDED_SIZE_CLASSES=$(CONFIG_EXTENDED_SIZE_CLASSES)
|
||||||
|
|
||||||
|
SHARED_FLAGS := -O3
|
||||||
|
|
||||||
|
CFLAGS := -std=c17 $(SHARED_FLAGS) -Wmissing-prototypes
|
||||||
|
CXXFLAGS := -std=c++17 -fsized-deallocation $(SHARED_FLAGS)
|
||||||
|
LDFLAGS := -Wl,-L$(dir)../out,-R,$(dir)../out
|
||||||
|
|
||||||
|
LDLIBS := -lpthread -lhardened_malloc
|
||||||
|
|
||||||
|
EXECUTABLES := \
|
||||||
|
offset \
|
||||||
|
mallinfo \
|
||||||
|
mallinfo2 \
|
||||||
|
malloc_info \
|
||||||
|
large_array_growth \
|
||||||
|
double_free_large \
|
||||||
|
double_free_large_delayed \
|
||||||
|
double_free_small \
|
||||||
|
double_free_small_delayed \
|
||||||
|
unaligned_free_large \
|
||||||
|
unaligned_free_small \
|
||||||
|
read_after_free_large \
|
||||||
|
read_after_free_small \
|
||||||
|
write_after_free_large \
|
||||||
|
write_after_free_large_reuse \
|
||||||
|
write_after_free_small \
|
||||||
|
write_after_free_small_reuse \
|
||||||
|
read_zero_size \
|
||||||
|
write_zero_size \
|
||||||
|
invalid_free_protected \
|
||||||
|
invalid_free_unprotected \
|
||||||
|
invalid_free_small_region \
|
||||||
|
invalid_free_small_region_far \
|
||||||
|
uninitialized_read_small \
|
||||||
|
uninitialized_read_large \
|
||||||
|
uninitialized_free \
|
||||||
|
uninitialized_realloc \
|
||||||
|
uninitialized_malloc_usable_size \
|
||||||
|
overflow_large_1_byte \
|
||||||
|
overflow_large_8_byte \
|
||||||
|
overflow_small_1_byte \
|
||||||
|
overflow_small_8_byte \
|
||||||
|
string_overflow \
|
||||||
|
delete_type_size_mismatch \
|
||||||
|
unaligned_malloc_usable_size_small \
|
||||||
|
invalid_malloc_usable_size_small \
|
||||||
|
invalid_malloc_usable_size_small_quarantine \
|
||||||
|
malloc_object_size \
|
||||||
|
malloc_object_size_offset \
|
||||||
|
invalid_malloc_object_size_small \
|
||||||
|
invalid_malloc_object_size_small_quarantine \
|
||||||
|
impossibly_large_malloc \
|
||||||
|
realloc_init
|
||||||
|
|
||||||
|
all: $(EXECUTABLES)
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f $(EXECUTABLES)
|
||||||
|
rm -fr ./__pycache__
|
0
src/hardened_malloc/test/__init__.py
Normal file
0
src/hardened_malloc/test/__init__.py
Normal file
14
src/hardened_malloc/test/delete_type_size_mismatch.cc
Normal file
14
src/hardened_malloc/test/delete_type_size_mismatch.cc
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "test_util.h"
|
||||||
|
|
||||||
|
struct foo {
|
||||||
|
uint64_t a, b, c, d;
|
||||||
|
};
|
||||||
|
|
||||||
|
OPTNONE int main(void) {
|
||||||
|
void *p = new char;
|
||||||
|
struct foo *c = (struct foo *)p;
|
||||||
|
delete c;
|
||||||
|
return 0;
|
||||||
|
}
|
13
src/hardened_malloc/test/double_free_large.c
Normal file
13
src/hardened_malloc/test/double_free_large.c
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "test_util.h"
|
||||||
|
|
||||||
|
OPTNONE int main(void) {
|
||||||
|
void *p = malloc(256 * 1024);
|
||||||
|
if (!p) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
free(p);
|
||||||
|
free(p);
|
||||||
|
return 0;
|
||||||
|
}
|
18
src/hardened_malloc/test/double_free_large_delayed.c
Normal file
18
src/hardened_malloc/test/double_free_large_delayed.c
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "test_util.h"
|
||||||
|
|
||||||
|
OPTNONE int main(void) {
|
||||||
|
void *p = malloc(256 * 1024);
|
||||||
|
if (!p) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
void *q = malloc(256 * 1024);
|
||||||
|
if (!q) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
free(p);
|
||||||
|
free(q);
|
||||||
|
free(p);
|
||||||
|
return 0;
|
||||||
|
}
|
13
src/hardened_malloc/test/double_free_small.c
Normal file
13
src/hardened_malloc/test/double_free_small.c
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "test_util.h"
|
||||||
|
|
||||||
|
OPTNONE int main(void) {
|
||||||
|
void *p = malloc(16);
|
||||||
|
if (!p) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
free(p);
|
||||||
|
free(p);
|
||||||
|
return 0;
|
||||||
|
}
|
18
src/hardened_malloc/test/double_free_small_delayed.c
Normal file
18
src/hardened_malloc/test/double_free_small_delayed.c
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "test_util.h"
|
||||||
|
|
||||||
|
OPTNONE int main(void) {
|
||||||
|
void *p = malloc(16);
|
||||||
|
if (!p) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
void *q = malloc(16);
|
||||||
|
if (!q) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
free(p);
|
||||||
|
free(q);
|
||||||
|
free(p);
|
||||||
|
return 0;
|
||||||
|
}
|
8
src/hardened_malloc/test/impossibly_large_malloc.c
Normal file
8
src/hardened_malloc/test/impossibly_large_malloc.c
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "test_util.h"
|
||||||
|
|
||||||
|
OPTNONE int main(void) {
|
||||||
|
char *p = malloc(-8);
|
||||||
|
return !(p == NULL);
|
||||||
|
}
|
15
src/hardened_malloc/test/invalid_free_protected.c
Normal file
15
src/hardened_malloc/test/invalid_free_protected.c
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include <sys/mman.h>
|
||||||
|
|
||||||
|
#include "test_util.h"
|
||||||
|
|
||||||
|
OPTNONE int main(void) {
|
||||||
|
free(malloc(16));
|
||||||
|
char *p = mmap(NULL, 4096 * 16, PROT_NONE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
|
||||||
|
if (p == MAP_FAILED) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
free(p + 4096 * 8);
|
||||||
|
return 0;
|
||||||
|
}
|
13
src/hardened_malloc/test/invalid_free_small_region.c
Normal file
13
src/hardened_malloc/test/invalid_free_small_region.c
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "test_util.h"
|
||||||
|
|
||||||
|
OPTNONE int main(void) {
|
||||||
|
char *p = malloc(16);
|
||||||
|
if (!p) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
char *q = p + 4096 * 4;
|
||||||
|
free(q);
|
||||||
|
return 0;
|
||||||
|
}
|
13
src/hardened_malloc/test/invalid_free_small_region_far.c
Normal file
13
src/hardened_malloc/test/invalid_free_small_region_far.c
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "test_util.h"
|
||||||
|
|
||||||
|
OPTNONE int main(void) {
|
||||||
|
char *p = malloc(16);
|
||||||
|
if (!p) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
char *q = p + 1024 * 1024 * 1024;
|
||||||
|
free(q);
|
||||||
|
return 0;
|
||||||
|
}
|
15
src/hardened_malloc/test/invalid_free_unprotected.c
Normal file
15
src/hardened_malloc/test/invalid_free_unprotected.c
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include <sys/mman.h>
|
||||||
|
|
||||||
|
#include "test_util.h"
|
||||||
|
|
||||||
|
OPTNONE int main(void) {
|
||||||
|
free(malloc(16));
|
||||||
|
char *p = mmap(NULL, 4096 * 16, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
|
||||||
|
if (p == MAP_FAILED) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
free(p + 4096 * 8);
|
||||||
|
return 0;
|
||||||
|
}
|
15
src/hardened_malloc/test/invalid_malloc_object_size_small.c
Normal file
15
src/hardened_malloc/test/invalid_malloc_object_size_small.c
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "test_util.h"
|
||||||
|
|
||||||
|
size_t malloc_object_size(void *ptr);
|
||||||
|
|
||||||
|
OPTNONE int main(void) {
|
||||||
|
char *p = malloc(16);
|
||||||
|
if (!p) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
char *q = p + 4096 * 4;
|
||||||
|
malloc_object_size(q);
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "test_util.h"
|
||||||
|
|
||||||
|
size_t malloc_object_size(void *ptr);
|
||||||
|
|
||||||
|
OPTNONE int main(void) {
|
||||||
|
void *p = malloc(16);
|
||||||
|
if (!p) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
free(p);
|
||||||
|
malloc_object_size(p);
|
||||||
|
return 0;
|
||||||
|
}
|
13
src/hardened_malloc/test/invalid_malloc_usable_size_small.c
Normal file
13
src/hardened_malloc/test/invalid_malloc_usable_size_small.c
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
#include <malloc.h>
|
||||||
|
|
||||||
|
#include "test_util.h"
|
||||||
|
|
||||||
|
OPTNONE int main(void) {
|
||||||
|
char *p = malloc(16);
|
||||||
|
if (!p) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
char *q = p + 4096 * 4;
|
||||||
|
malloc_usable_size(q);
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
#include <malloc.h>
|
||||||
|
|
||||||
|
#include "test_util.h"
|
||||||
|
|
||||||
|
OPTNONE int main(void) {
|
||||||
|
void *p = malloc(16);
|
||||||
|
if (!p) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
free(p);
|
||||||
|
malloc_usable_size(p);
|
||||||
|
return 0;
|
||||||
|
}
|
18
src/hardened_malloc/test/large_array_growth.c
Normal file
18
src/hardened_malloc/test/large_array_growth.c
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "test_util.h"
|
||||||
|
|
||||||
|
OPTNONE int main(void) {
|
||||||
|
void *p = NULL;
|
||||||
|
size_t size = 256 * 1024;
|
||||||
|
|
||||||
|
for (unsigned i = 0; i < 20; i++) {
|
||||||
|
p = realloc(p, size);
|
||||||
|
if (!p) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
memset(p, 'a', size);
|
||||||
|
size = size * 3 / 2;
|
||||||
|
}
|
||||||
|
}
|
44
src/hardened_malloc/test/mallinfo.c
Normal file
44
src/hardened_malloc/test/mallinfo.c
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#if defined(__GLIBC__) || defined(__ANDROID__)
|
||||||
|
#include <malloc.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "test_util.h"
|
||||||
|
|
||||||
|
static void print_mallinfo(void) {
|
||||||
|
#if defined(__GLIBC__) || defined(__ANDROID__)
|
||||||
|
struct mallinfo info = mallinfo();
|
||||||
|
printf("mallinfo:\n");
|
||||||
|
printf("arena: %zu\n", (size_t)info.arena);
|
||||||
|
printf("ordblks: %zu\n", (size_t)info.ordblks);
|
||||||
|
printf("smblks: %zu\n", (size_t)info.smblks);
|
||||||
|
printf("hblks: %zu\n", (size_t)info.hblks);
|
||||||
|
printf("hblkhd: %zu\n", (size_t)info.hblkhd);
|
||||||
|
printf("usmblks: %zu\n", (size_t)info.usmblks);
|
||||||
|
printf("fsmblks: %zu\n", (size_t)info.fsmblks);
|
||||||
|
printf("uordblks: %zu\n", (size_t)info.uordblks);
|
||||||
|
printf("fordblks: %zu\n", (size_t)info.fordblks);
|
||||||
|
printf("keepcost: %zu\n", (size_t)info.keepcost);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
OPTNONE int main(void) {
|
||||||
|
void *a[4];
|
||||||
|
|
||||||
|
a[0] = malloc(1024 * 1024 * 1024);
|
||||||
|
a[1] = malloc(16);
|
||||||
|
a[2] = malloc(32);
|
||||||
|
a[3] = malloc(64);
|
||||||
|
|
||||||
|
print_mallinfo();
|
||||||
|
|
||||||
|
free(a[0]);
|
||||||
|
free(a[1]);
|
||||||
|
free(a[2]);
|
||||||
|
free(a[3]);
|
||||||
|
|
||||||
|
printf("\n");
|
||||||
|
print_mallinfo();
|
||||||
|
}
|
44
src/hardened_malloc/test/mallinfo2.c
Normal file
44
src/hardened_malloc/test/mallinfo2.c
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#if defined(__GLIBC__)
|
||||||
|
#include <malloc.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "test_util.h"
|
||||||
|
|
||||||
|
static void print_mallinfo2(void) {
|
||||||
|
#if defined(__GLIBC__)
|
||||||
|
struct mallinfo2 info = mallinfo2();
|
||||||
|
printf("mallinfo2:\n");
|
||||||
|
printf("arena: %zu\n", (size_t)info.arena);
|
||||||
|
printf("ordblks: %zu\n", (size_t)info.ordblks);
|
||||||
|
printf("smblks: %zu\n", (size_t)info.smblks);
|
||||||
|
printf("hblks: %zu\n", (size_t)info.hblks);
|
||||||
|
printf("hblkhd: %zu\n", (size_t)info.hblkhd);
|
||||||
|
printf("usmblks: %zu\n", (size_t)info.usmblks);
|
||||||
|
printf("fsmblks: %zu\n", (size_t)info.fsmblks);
|
||||||
|
printf("uordblks: %zu\n", (size_t)info.uordblks);
|
||||||
|
printf("fordblks: %zu\n", (size_t)info.fordblks);
|
||||||
|
printf("keepcost: %zu\n", (size_t)info.keepcost);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
OPTNONE int main(void) {
|
||||||
|
void *a[4];
|
||||||
|
|
||||||
|
a[0] = malloc(1024 * 1024 * 1024);
|
||||||
|
a[1] = malloc(16);
|
||||||
|
a[2] = malloc(32);
|
||||||
|
a[3] = malloc(64);
|
||||||
|
|
||||||
|
print_mallinfo2();
|
||||||
|
|
||||||
|
free(a[0]);
|
||||||
|
free(a[1]);
|
||||||
|
free(a[2]);
|
||||||
|
free(a[3]);
|
||||||
|
|
||||||
|
printf("\n");
|
||||||
|
print_mallinfo2();
|
||||||
|
}
|
35
src/hardened_malloc/test/malloc_info.c
Normal file
35
src/hardened_malloc/test/malloc_info.c
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#if defined(__GLIBC__) || defined(__ANDROID__)
|
||||||
|
#include <malloc.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "test_util.h"
|
||||||
|
#include "../util.h"
|
||||||
|
|
||||||
|
OPTNONE static void leak_memory(void) {
|
||||||
|
(void)!malloc(1024 * 1024 * 1024);
|
||||||
|
(void)!malloc(16);
|
||||||
|
(void)!malloc(32);
|
||||||
|
(void)!malloc(4096);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *do_work(UNUSED void *p) {
|
||||||
|
leak_memory();
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
pthread_t thread[4];
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
pthread_create(&thread[i], NULL, do_work, NULL);
|
||||||
|
}
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
pthread_join(thread[i], NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(__GLIBC__) || defined(__ANDROID__)
|
||||||
|
malloc_info(0, stdout);
|
||||||
|
#endif
|
||||||
|
}
|
12
src/hardened_malloc/test/malloc_object_size.c
Normal file
12
src/hardened_malloc/test/malloc_object_size.c
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "test_util.h"
|
||||||
|
|
||||||
|
size_t malloc_object_size(void *ptr);
|
||||||
|
|
||||||
|
OPTNONE int main(void) {
|
||||||
|
char *p = malloc(16);
|
||||||
|
size_t size = malloc_object_size(p);
|
||||||
|
return size != (SLAB_CANARY ? 24 : 32);
|
||||||
|
}
|
12
src/hardened_malloc/test/malloc_object_size_offset.c
Normal file
12
src/hardened_malloc/test/malloc_object_size_offset.c
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "test_util.h"
|
||||||
|
|
||||||
|
size_t malloc_object_size(void *ptr);
|
||||||
|
|
||||||
|
OPTNONE int main(void) {
|
||||||
|
char *p = malloc(16);
|
||||||
|
size_t size = malloc_object_size(p + 5);
|
||||||
|
return size != (SLAB_CANARY ? 19 : 27);
|
||||||
|
}
|
50
src/hardened_malloc/test/offset.c
Normal file
50
src/hardened_malloc/test/offset.c
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
static size_t size_classes[] = {
|
||||||
|
/* large */ 4 * 1024 * 1024,
|
||||||
|
/* 0 */ 0,
|
||||||
|
/* 16 */ 16, 32, 48, 64, 80, 96, 112, 128,
|
||||||
|
/* 32 */ 160, 192, 224, 256,
|
||||||
|
/* 64 */ 320, 384, 448, 512,
|
||||||
|
/* 128 */ 640, 768, 896, 1024,
|
||||||
|
/* 256 */ 1280, 1536, 1792, 2048,
|
||||||
|
/* 512 */ 2560, 3072, 3584, 4096,
|
||||||
|
/* 1024 */ 5120, 6144, 7168, 8192,
|
||||||
|
/* 2048 */ 10240, 12288, 14336, 16384,
|
||||||
|
#if CONFIG_EXTENDED_SIZE_CLASSES
|
||||||
|
/* 4096 */ 20480, 24576, 28672, 32768,
|
||||||
|
/* 8192 */ 40960, 49152, 57344, 65536,
|
||||||
|
/* 16384 */ 81920, 98304, 114688, 131072,
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
#define N_SIZE_CLASSES (sizeof(size_classes) / sizeof(size_classes[0]))
|
||||||
|
|
||||||
|
static const size_t canary_size = SLAB_CANARY ? sizeof(uint64_t) : 0;
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
for (unsigned i = 2; i < N_SIZE_CLASSES; i++) {
|
||||||
|
size_classes[i] -= canary_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *p[N_SIZE_CLASSES];
|
||||||
|
for (unsigned i = 0; i < N_SIZE_CLASSES; i++) {
|
||||||
|
size_t size = size_classes[i];
|
||||||
|
p[i] = malloc(size);
|
||||||
|
if (!p[i]) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
void *q = malloc(size);
|
||||||
|
if (!q) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (i != 0) {
|
||||||
|
printf("%zu to %zu: %zd\n", size_classes[i - 1], size, p[i] - p[i - 1]);
|
||||||
|
}
|
||||||
|
printf("%zu to %zu: %zd\n", size, size, q - p[i]);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
15
src/hardened_malloc/test/overflow_large_1_byte.c
Normal file
15
src/hardened_malloc/test/overflow_large_1_byte.c
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
#include <malloc.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "test_util.h"
|
||||||
|
|
||||||
|
OPTNONE int main(void) {
|
||||||
|
char *p = malloc(256 * 1024);
|
||||||
|
if (!p) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
size_t size = malloc_usable_size(p);
|
||||||
|
*(p + size) = 0;
|
||||||
|
free(p);
|
||||||
|
return 0;
|
||||||
|
}
|
15
src/hardened_malloc/test/overflow_large_8_byte.c
Normal file
15
src/hardened_malloc/test/overflow_large_8_byte.c
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
#include <malloc.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "test_util.h"
|
||||||
|
|
||||||
|
OPTNONE int main(void) {
|
||||||
|
char *p = malloc(256 * 1024);
|
||||||
|
if (!p) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
size_t size = malloc_usable_size(p);
|
||||||
|
*(p + size + 7) = 0;
|
||||||
|
free(p);
|
||||||
|
return 0;
|
||||||
|
}
|
15
src/hardened_malloc/test/overflow_small_1_byte.c
Normal file
15
src/hardened_malloc/test/overflow_small_1_byte.c
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
#include <malloc.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "test_util.h"
|
||||||
|
|
||||||
|
OPTNONE int main(void) {
|
||||||
|
char *p = malloc(8);
|
||||||
|
if (!p) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
size_t size = malloc_usable_size(p);
|
||||||
|
*(p + size) = 1;
|
||||||
|
free(p);
|
||||||
|
return 0;
|
||||||
|
}
|
16
src/hardened_malloc/test/overflow_small_8_byte.c
Normal file
16
src/hardened_malloc/test/overflow_small_8_byte.c
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
#include <malloc.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "test_util.h"
|
||||||
|
|
||||||
|
OPTNONE int main(void) {
|
||||||
|
char *p = malloc(8);
|
||||||
|
if (!p) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
size_t size = malloc_usable_size(p);
|
||||||
|
// XOR is used to avoid the test having a 1/256 chance to fail
|
||||||
|
*(p + size + 7) ^= 1;
|
||||||
|
free(p);
|
||||||
|
return 0;
|
||||||
|
}
|
21
src/hardened_malloc/test/read_after_free_large.c
Normal file
21
src/hardened_malloc/test/read_after_free_large.c
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "test_util.h"
|
||||||
|
|
||||||
|
OPTNONE int main(void) {
|
||||||
|
char *p = malloc(256 * 1024);
|
||||||
|
if (!p) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
memset(p, 'a', 16);
|
||||||
|
free(p);
|
||||||
|
for (size_t i = 0; i < 256 * 1024; i++) {
|
||||||
|
printf("%x\n", p[i]);
|
||||||
|
if (p[i] != '\0') {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
21
src/hardened_malloc/test/read_after_free_small.c
Normal file
21
src/hardened_malloc/test/read_after_free_small.c
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "test_util.h"
|
||||||
|
|
||||||
|
OPTNONE int main(void) {
|
||||||
|
char *p = malloc(16);
|
||||||
|
if (!p) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
memset(p, 'a', 16);
|
||||||
|
free(p);
|
||||||
|
for (size_t i = 0; i < 16; i++) {
|
||||||
|
printf("%x\n", p[i]);
|
||||||
|
if (p[i] != '\0') {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
13
src/hardened_malloc/test/read_zero_size.c
Normal file
13
src/hardened_malloc/test/read_zero_size.c
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "test_util.h"
|
||||||
|
|
||||||
|
OPTNONE int main(void) {
|
||||||
|
char *p = malloc(0);
|
||||||
|
if (!p) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
printf("%c\n", *p);
|
||||||
|
return 0;
|
||||||
|
}
|
33
src/hardened_malloc/test/realloc_init.c
Normal file
33
src/hardened_malloc/test/realloc_init.c
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
static void *thread_func(void *arg) {
|
||||||
|
arg = realloc(arg, 1024);
|
||||||
|
if (!arg) {
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(arg);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
void *mem = realloc(NULL, 12);
|
||||||
|
if (!mem) {
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_t thread;
|
||||||
|
int r = pthread_create(&thread, NULL, thread_func, mem);
|
||||||
|
if (r != 0) {
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = pthread_join(thread, NULL);
|
||||||
|
if (r != 0) {
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
20
src/hardened_malloc/test/string_overflow.c
Normal file
20
src/hardened_malloc/test/string_overflow.c
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <malloc.h>
|
||||||
|
|
||||||
|
#include "test_util.h"
|
||||||
|
|
||||||
|
OPTNONE int main(void) {
|
||||||
|
char *p = malloc(16);
|
||||||
|
if (!p) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t size = malloc_usable_size(p);
|
||||||
|
memset(p, 'a', size);
|
||||||
|
printf("overflow by %zu bytes\n", strlen(p) - size);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
242
src/hardened_malloc/test/test_smc.py
Normal file
242
src/hardened_malloc/test/test_smc.py
Normal file
|
@ -0,0 +1,242 @@
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
|
||||||
|
class TestSimpleMemoryCorruption(unittest.TestCase):
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(self):
|
||||||
|
self.dir = os.path.dirname(os.path.realpath(__file__))
|
||||||
|
|
||||||
|
def run_test(self, test_name):
|
||||||
|
sub = subprocess.Popen(self.dir + "/" + test_name,
|
||||||
|
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||||
|
stdout, stderr = sub.communicate()
|
||||||
|
return stdout, stderr, sub.returncode
|
||||||
|
|
||||||
|
def test_delete_type_size_mismatch(self):
|
||||||
|
_stdout, stderr, returncode = self.run_test(
|
||||||
|
"delete_type_size_mismatch")
|
||||||
|
self.assertEqual(returncode, -6)
|
||||||
|
self.assertEqual(stderr.decode(
|
||||||
|
"utf-8"), "fatal allocator error: sized deallocation mismatch (small)\n")
|
||||||
|
|
||||||
|
def test_double_free_large_delayed(self):
|
||||||
|
_stdout, stderr, returncode = self.run_test(
|
||||||
|
"double_free_large_delayed")
|
||||||
|
self.assertEqual(returncode, -6)
|
||||||
|
self.assertEqual(stderr.decode("utf-8"),
|
||||||
|
"fatal allocator error: invalid free\n")
|
||||||
|
|
||||||
|
def test_double_free_large(self):
|
||||||
|
_stdout, stderr, returncode = self.run_test("double_free_large")
|
||||||
|
self.assertEqual(returncode, -6)
|
||||||
|
self.assertEqual(stderr.decode("utf-8"),
|
||||||
|
"fatal allocator error: invalid free\n")
|
||||||
|
|
||||||
|
def test_double_free_small_delayed(self):
|
||||||
|
_stdout, stderr, returncode = self.run_test(
|
||||||
|
"double_free_small_delayed")
|
||||||
|
self.assertEqual(returncode, -6)
|
||||||
|
self.assertEqual(stderr.decode("utf-8"),
|
||||||
|
"fatal allocator error: double free (quarantine)\n")
|
||||||
|
|
||||||
|
def test_double_free_small(self):
|
||||||
|
_stdout, stderr, returncode = self.run_test("double_free_small")
|
||||||
|
self.assertEqual(returncode, -6)
|
||||||
|
self.assertEqual(stderr.decode("utf-8"),
|
||||||
|
"fatal allocator error: double free (quarantine)\n")
|
||||||
|
|
||||||
|
def test_overflow_large_1_byte(self):
|
||||||
|
_stdout, _stderr, returncode = self.run_test(
|
||||||
|
"overflow_large_1_byte")
|
||||||
|
self.assertEqual(returncode, -11)
|
||||||
|
|
||||||
|
def test_overflow_large_8_byte(self):
|
||||||
|
_stdout, _stderr, returncode = self.run_test(
|
||||||
|
"overflow_large_8_byte")
|
||||||
|
self.assertEqual(returncode, -11)
|
||||||
|
|
||||||
|
def test_overflow_small_1_byte(self):
|
||||||
|
_stdout, stderr, returncode = self.run_test(
|
||||||
|
"overflow_small_1_byte")
|
||||||
|
self.assertEqual(returncode, -6)
|
||||||
|
self.assertEqual(stderr.decode("utf-8"),
|
||||||
|
"fatal allocator error: canary corrupted\n")
|
||||||
|
|
||||||
|
def test_overflow_small_8_byte(self):
|
||||||
|
_stdout, stderr, returncode = self.run_test(
|
||||||
|
"overflow_small_8_byte")
|
||||||
|
self.assertEqual(returncode, -6)
|
||||||
|
self.assertEqual(stderr.decode("utf-8"),
|
||||||
|
"fatal allocator error: canary corrupted\n")
|
||||||
|
|
||||||
|
def test_invalid_free_protected(self):
|
||||||
|
_stdout, stderr, returncode = self.run_test("invalid_free_protected")
|
||||||
|
self.assertEqual(returncode, -6)
|
||||||
|
self.assertEqual(stderr.decode("utf-8"),
|
||||||
|
"fatal allocator error: invalid free\n")
|
||||||
|
|
||||||
|
def test_invalid_free_small_region_far(self):
|
||||||
|
_stdout, stderr, returncode = self.run_test(
|
||||||
|
"invalid_free_small_region_far")
|
||||||
|
self.assertEqual(returncode, -6)
|
||||||
|
self.assertEqual(stderr.decode(
|
||||||
|
"utf-8"), "fatal allocator error: invalid free within a slab yet to be used\n")
|
||||||
|
|
||||||
|
def test_invalid_free_small_region(self):
|
||||||
|
_stdout, stderr, returncode = self.run_test(
|
||||||
|
"invalid_free_small_region")
|
||||||
|
self.assertEqual(returncode, -6)
|
||||||
|
self.assertEqual(stderr.decode("utf-8"),
|
||||||
|
"fatal allocator error: double free\n")
|
||||||
|
|
||||||
|
def test_invalid_free_unprotected(self):
|
||||||
|
_stdout, stderr, returncode = self.run_test("invalid_free_unprotected")
|
||||||
|
self.assertEqual(returncode, -6)
|
||||||
|
self.assertEqual(stderr.decode("utf-8"),
|
||||||
|
"fatal allocator error: invalid free\n")
|
||||||
|
|
||||||
|
def test_invalid_malloc_usable_size_small_quarantene(self):
|
||||||
|
_stdout, stderr, returncode = self.run_test(
|
||||||
|
"invalid_malloc_usable_size_small_quarantine")
|
||||||
|
self.assertEqual(returncode, -6)
|
||||||
|
self.assertEqual(stderr.decode(
|
||||||
|
"utf-8"), "fatal allocator error: invalid malloc_usable_size (quarantine)\n")
|
||||||
|
|
||||||
|
def test_invalid_malloc_usable_size_small(self):
|
||||||
|
_stdout, stderr, returncode = self.run_test(
|
||||||
|
"invalid_malloc_usable_size_small")
|
||||||
|
self.assertEqual(returncode, -6)
|
||||||
|
self.assertEqual(stderr.decode(
|
||||||
|
"utf-8"), "fatal allocator error: invalid malloc_usable_size\n")
|
||||||
|
|
||||||
|
def test_read_after_free_large(self):
|
||||||
|
_stdout, _stderr, returncode = self.run_test("read_after_free_large")
|
||||||
|
self.assertEqual(returncode, -11)
|
||||||
|
|
||||||
|
def test_read_after_free_small(self):
|
||||||
|
stdout, _stderr, returncode = self.run_test("read_after_free_small")
|
||||||
|
self.assertEqual(returncode, 0)
|
||||||
|
self.assertEqual(stdout.decode("utf-8"),
|
||||||
|
"0\n0\n0\n0\n0\n0\n0\n0\n0\n0\n0\n0\n0\n0\n0\n0\n")
|
||||||
|
|
||||||
|
def test_read_zero_size(self):
|
||||||
|
_stdout, _stderr, returncode = self.run_test("read_zero_size")
|
||||||
|
self.assertEqual(returncode, -11)
|
||||||
|
|
||||||
|
def test_string_overflow(self):
|
||||||
|
stdout, _stderr, returncode = self.run_test("string_overflow")
|
||||||
|
self.assertEqual(returncode, 0)
|
||||||
|
self.assertEqual(stdout.decode("utf-8"), "overflow by 0 bytes\n")
|
||||||
|
|
||||||
|
def test_unaligned_free_large(self):
|
||||||
|
_stdout, stderr, returncode = self.run_test("unaligned_free_large")
|
||||||
|
self.assertEqual(returncode, -6)
|
||||||
|
self.assertEqual(stderr.decode("utf-8"),
|
||||||
|
"fatal allocator error: invalid free\n")
|
||||||
|
|
||||||
|
def test_unaligned_free_small(self):
|
||||||
|
_stdout, stderr, returncode = self.run_test("unaligned_free_small")
|
||||||
|
self.assertEqual(returncode, -6)
|
||||||
|
self.assertEqual(stderr.decode("utf-8"),
|
||||||
|
"fatal allocator error: invalid unaligned free\n")
|
||||||
|
|
||||||
|
def test_unaligned_malloc_usable_size_small(self):
|
||||||
|
_stdout, stderr, returncode = self.run_test(
|
||||||
|
"unaligned_malloc_usable_size_small")
|
||||||
|
self.assertEqual(returncode, -6)
|
||||||
|
self.assertEqual(stderr.decode("utf-8"),
|
||||||
|
"fatal allocator error: invalid unaligned malloc_usable_size\n")
|
||||||
|
|
||||||
|
def test_uninitialized_free(self):
|
||||||
|
_stdout, stderr, returncode = self.run_test("uninitialized_free")
|
||||||
|
self.assertEqual(returncode, -6)
|
||||||
|
self.assertEqual(stderr.decode("utf-8"),
|
||||||
|
"fatal allocator error: invalid free\n")
|
||||||
|
|
||||||
|
def test_uninitialized_malloc_usable_size(self):
|
||||||
|
_stdout, stderr, returncode = self.run_test(
|
||||||
|
"uninitialized_malloc_usable_size")
|
||||||
|
self.assertEqual(returncode, -6)
|
||||||
|
self.assertEqual(stderr.decode("utf-8"),
|
||||||
|
"fatal allocator error: invalid malloc_usable_size\n")
|
||||||
|
|
||||||
|
def test_uninitialized_realloc(self):
|
||||||
|
_stdout, stderr, returncode = self.run_test("uninitialized_realloc")
|
||||||
|
self.assertEqual(returncode, -6)
|
||||||
|
self.assertEqual(stderr.decode("utf-8"),
|
||||||
|
"fatal allocator error: invalid realloc\n")
|
||||||
|
|
||||||
|
def test_write_after_free_large_reuse(self):
|
||||||
|
_stdout, _stderr, returncode = self.run_test(
|
||||||
|
"write_after_free_large_reuse")
|
||||||
|
self.assertEqual(returncode, -11)
|
||||||
|
|
||||||
|
def test_write_after_free_large(self):
|
||||||
|
_stdout, _stderr, returncode = self.run_test("write_after_free_large")
|
||||||
|
self.assertEqual(returncode, -11)
|
||||||
|
|
||||||
|
def test_write_after_free_small_reuse(self):
|
||||||
|
_stdout, stderr, returncode = self.run_test(
|
||||||
|
"write_after_free_small_reuse")
|
||||||
|
self.assertEqual(returncode, -6)
|
||||||
|
self.assertEqual(stderr.decode("utf-8"),
|
||||||
|
"fatal allocator error: detected write after free\n")
|
||||||
|
|
||||||
|
def test_write_after_free_small(self):
|
||||||
|
_stdout, stderr, returncode = self.run_test("write_after_free_small")
|
||||||
|
self.assertEqual(returncode, -6)
|
||||||
|
self.assertEqual(stderr.decode("utf-8"),
|
||||||
|
"fatal allocator error: detected write after free\n")
|
||||||
|
|
||||||
|
def test_write_zero_size(self):
|
||||||
|
_stdout, _stderr, returncode = self.run_test("write_zero_size")
|
||||||
|
self.assertEqual(returncode, -11)
|
||||||
|
|
||||||
|
def test_malloc_object_size(self):
|
||||||
|
_stdout, _stderr, returncode = self.run_test("malloc_object_size")
|
||||||
|
self.assertEqual(returncode, 0)
|
||||||
|
|
||||||
|
def test_malloc_object_size_offset(self):
|
||||||
|
_stdout, _stderr, returncode = self.run_test(
|
||||||
|
"malloc_object_size_offset")
|
||||||
|
self.assertEqual(returncode, 0)
|
||||||
|
|
||||||
|
def test_invalid_malloc_object_size_small(self):
|
||||||
|
_stdout, stderr, returncode = self.run_test(
|
||||||
|
"invalid_malloc_object_size_small")
|
||||||
|
self.assertEqual(returncode, -6)
|
||||||
|
self.assertEqual(stderr.decode(
|
||||||
|
"utf-8"), "fatal allocator error: invalid malloc_object_size\n")
|
||||||
|
|
||||||
|
def test_invalid_malloc_object_size_small_quarantine(self):
|
||||||
|
_stdout, stderr, returncode = self.run_test(
|
||||||
|
"invalid_malloc_object_size_small_quarantine")
|
||||||
|
self.assertEqual(returncode, -6)
|
||||||
|
self.assertEqual(stderr.decode(
|
||||||
|
"utf-8"), "fatal allocator error: invalid malloc_object_size (quarantine)\n")
|
||||||
|
|
||||||
|
def test_impossibly_large_malloc(self):
|
||||||
|
_stdout, stderr, returncode = self.run_test(
|
||||||
|
"impossibly_large_malloc")
|
||||||
|
self.assertEqual(returncode, 0)
|
||||||
|
|
||||||
|
def test_uninitialized_read_small(self):
|
||||||
|
_stdout, stderr, returncode = self.run_test(
|
||||||
|
"uninitialized_read_small")
|
||||||
|
self.assertEqual(returncode, 0)
|
||||||
|
|
||||||
|
def test_uninitialized_read_large(self):
|
||||||
|
_stdout, stderr, returncode = self.run_test(
|
||||||
|
"uninitialized_read_large")
|
||||||
|
self.assertEqual(returncode, 0)
|
||||||
|
|
||||||
|
def test_realloc_init(self):
|
||||||
|
_stdout, _stderr, returncode = self.run_test(
|
||||||
|
"realloc_init")
|
||||||
|
self.assertEqual(returncode, 0)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
10
src/hardened_malloc/test/test_util.h
Normal file
10
src/hardened_malloc/test/test_util.h
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
#ifndef TEST_UTIL_H
|
||||||
|
#define TEST_UTIL_H
|
||||||
|
|
||||||
|
#ifdef __clang__
|
||||||
|
#define OPTNONE __attribute__((optnone))
|
||||||
|
#else
|
||||||
|
#define OPTNONE __attribute__((optimize(0)))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
12
src/hardened_malloc/test/unaligned_free_large.c
Normal file
12
src/hardened_malloc/test/unaligned_free_large.c
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "test_util.h"
|
||||||
|
|
||||||
|
OPTNONE int main(void) {
|
||||||
|
char *p = malloc(256 * 1024);
|
||||||
|
if (!p) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
free(p + 1);
|
||||||
|
return 0;
|
||||||
|
}
|
12
src/hardened_malloc/test/unaligned_free_small.c
Normal file
12
src/hardened_malloc/test/unaligned_free_small.c
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "test_util.h"
|
||||||
|
|
||||||
|
OPTNONE int main(void) {
|
||||||
|
char *p = malloc(16);
|
||||||
|
if (!p) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
free(p + 1);
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
#include <malloc.h>
|
||||||
|
|
||||||
|
#include "test_util.h"
|
||||||
|
|
||||||
|
OPTNONE int main(void) {
|
||||||
|
char *p = malloc(16);
|
||||||
|
if (!p) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
malloc_usable_size(p + 1);
|
||||||
|
return 0;
|
||||||
|
}
|
8
src/hardened_malloc/test/uninitialized_free.c
Normal file
8
src/hardened_malloc/test/uninitialized_free.c
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "test_util.h"
|
||||||
|
|
||||||
|
OPTNONE int main(void) {
|
||||||
|
free((void *)1);
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
#include <malloc.h>
|
||||||
|
|
||||||
|
#include "test_util.h"
|
||||||
|
|
||||||
|
OPTNONE int main(void) {
|
||||||
|
malloc_usable_size((void *)1);
|
||||||
|
return 0;
|
||||||
|
}
|
14
src/hardened_malloc/test/uninitialized_read_large.c
Normal file
14
src/hardened_malloc/test/uninitialized_read_large.c
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "test_util.h"
|
||||||
|
|
||||||
|
OPTNONE int main(void) {
|
||||||
|
char *p = malloc(256 * 1024);
|
||||||
|
for (unsigned i = 0; i < 256 * 1024; i++) {
|
||||||
|
if (p[i] != 0) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free(p);
|
||||||
|
return 0;
|
||||||
|
}
|
14
src/hardened_malloc/test/uninitialized_read_small.c
Normal file
14
src/hardened_malloc/test/uninitialized_read_small.c
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "test_util.h"
|
||||||
|
|
||||||
|
OPTNONE int main(void) {
|
||||||
|
char *p = malloc(8);
|
||||||
|
for (unsigned i = 0; i < 8; i++) {
|
||||||
|
if (p[i] != 0) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free(p);
|
||||||
|
return 0;
|
||||||
|
}
|
11
src/hardened_malloc/test/uninitialized_realloc.c
Normal file
11
src/hardened_malloc/test/uninitialized_realloc.c
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "test_util.h"
|
||||||
|
|
||||||
|
OPTNONE int main(void) {
|
||||||
|
void *p = realloc((void *)1, 16);
|
||||||
|
if (!p) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
13
src/hardened_malloc/test/write_after_free_large.c
Normal file
13
src/hardened_malloc/test/write_after_free_large.c
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "test_util.h"
|
||||||
|
|
||||||
|
OPTNONE int main(void) {
|
||||||
|
char *p = malloc(256 * 1024);
|
||||||
|
if (!p) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
free(p);
|
||||||
|
p[64 * 1024 + 1] = 'a';
|
||||||
|
return 0;
|
||||||
|
}
|
16
src/hardened_malloc/test/write_after_free_large_reuse.c
Normal file
16
src/hardened_malloc/test/write_after_free_large_reuse.c
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "test_util.h"
|
||||||
|
#include "../util.h"
|
||||||
|
|
||||||
|
OPTNONE int main(void) {
|
||||||
|
char *p = malloc(256 * 1024);
|
||||||
|
if (!p) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
free(p);
|
||||||
|
UNUSED char *q = malloc(256 * 1024);
|
||||||
|
p[64 * 1024 + 1] = 'a';
|
||||||
|
return 0;
|
||||||
|
}
|
19
src/hardened_malloc/test/write_after_free_small.c
Normal file
19
src/hardened_malloc/test/write_after_free_small.c
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "test_util.h"
|
||||||
|
|
||||||
|
OPTNONE int main(void) {
|
||||||
|
char *p = malloc(128);
|
||||||
|
if (!p) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
free(p);
|
||||||
|
|
||||||
|
p[65] = 'a';
|
||||||
|
|
||||||
|
// trigger reuse of the allocation
|
||||||
|
for (size_t i = 0; i < 100000; i++) {
|
||||||
|
free(malloc(128));
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
21
src/hardened_malloc/test/write_after_free_small_reuse.c
Normal file
21
src/hardened_malloc/test/write_after_free_small_reuse.c
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "test_util.h"
|
||||||
|
#include "../util.h"
|
||||||
|
|
||||||
|
OPTNONE int main(void) {
|
||||||
|
char *p = malloc(128);
|
||||||
|
if (!p) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
free(p);
|
||||||
|
UNUSED char *q = malloc(128);
|
||||||
|
|
||||||
|
p[65] = 'a';
|
||||||
|
|
||||||
|
// trigger reuse of the allocation
|
||||||
|
for (size_t i = 0; i < 100000; i++) {
|
||||||
|
free(malloc(128));
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
12
src/hardened_malloc/test/write_zero_size.c
Normal file
12
src/hardened_malloc/test/write_zero_size.c
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "test_util.h"
|
||||||
|
|
||||||
|
OPTNONE int main(void) {
|
||||||
|
char *p = malloc(0);
|
||||||
|
if (!p) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
*p = 5;
|
||||||
|
return 0;
|
||||||
|
}
|
3126
src/hardened_malloc/third_party/libdivide.h
vendored
Normal file
3126
src/hardened_malloc/third_party/libdivide.h
vendored
Normal file
File diff suppressed because it is too large
Load diff
41
src/hardened_malloc/util.c
Normal file
41
src/hardened_malloc/util.c
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#ifdef __ANDROID__
|
||||||
|
#include <async_safe/log.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
#ifndef __ANDROID__
|
||||||
|
static int write_full(int fd, const char *buf, size_t length) {
|
||||||
|
do {
|
||||||
|
ssize_t bytes_written = write(fd, buf, length);
|
||||||
|
if (bytes_written == -1) {
|
||||||
|
if (errno == EINTR) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
buf += bytes_written;
|
||||||
|
length -= bytes_written;
|
||||||
|
} while (length);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
COLD noreturn void fatal_error(const char *s) {
|
||||||
|
#ifdef __ANDROID__
|
||||||
|
async_safe_fatal("hardened_malloc: fatal allocator error: %s", s);
|
||||||
|
#else
|
||||||
|
const char *prefix = "fatal allocator error: ";
|
||||||
|
(void)(write_full(STDERR_FILENO, prefix, strlen(prefix)) != -1 &&
|
||||||
|
write_full(STDERR_FILENO, s, strlen(s)) != -1 &&
|
||||||
|
write_full(STDERR_FILENO, "\n", 1));
|
||||||
|
abort();
|
||||||
|
#endif
|
||||||
|
}
|
88
src/hardened_malloc/util.h
Normal file
88
src/hardened_malloc/util.h
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
#ifndef UTIL_H
|
||||||
|
#define UTIL_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
// C11 noreturn doesn't work in C++
|
||||||
|
#define noreturn __attribute__((noreturn))
|
||||||
|
|
||||||
|
#define likely(x) __builtin_expect(!!(x), 1)
|
||||||
|
#define unlikely(x) __builtin_expect(!!(x), 0)
|
||||||
|
|
||||||
|
#define min(x, y) ({ \
|
||||||
|
__typeof__(x) _x = (x); \
|
||||||
|
__typeof__(y) _y = (y); \
|
||||||
|
(void) (&_x == &_y); \
|
||||||
|
_x < _y ? _x : _y; })
|
||||||
|
|
||||||
|
#define max(x, y) ({ \
|
||||||
|
__typeof__(x) _x = (x); \
|
||||||
|
__typeof__(y) _y = (y); \
|
||||||
|
(void) (&_x == &_y); \
|
||||||
|
_x > _y ? _x : _y; })
|
||||||
|
|
||||||
|
#define COLD __attribute__((cold))
|
||||||
|
#define UNUSED __attribute__((unused))
|
||||||
|
#define EXPORT __attribute__((visibility("default")))
|
||||||
|
|
||||||
|
#define STRINGIFY(s) #s
|
||||||
|
#define ALIAS(f) __attribute__((alias(STRINGIFY(f))))
|
||||||
|
|
||||||
|
typedef uint8_t u8;
|
||||||
|
typedef uint16_t u16;
|
||||||
|
typedef uint32_t u32;
|
||||||
|
typedef uint64_t u64;
|
||||||
|
typedef unsigned __int128 u128;
|
||||||
|
|
||||||
|
#define U64_WIDTH 64
|
||||||
|
|
||||||
|
static inline int ffz64(u64 x) {
|
||||||
|
return __builtin_ffsll(~x);
|
||||||
|
}
|
||||||
|
|
||||||
|
// parameter must not be 0
|
||||||
|
static inline int clz64(u64 x) {
|
||||||
|
return __builtin_clzll(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
// parameter must not be 0
|
||||||
|
static inline u64 log2u64(u64 x) {
|
||||||
|
return U64_WIDTH - clz64(x) - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline size_t align(size_t size, size_t align) {
|
||||||
|
size_t mask = align - 1;
|
||||||
|
return (size + mask) & ~mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
// u4_arr_{set,get} are helper functions for using u8 array as an array of unsigned 4-bit values.
|
||||||
|
|
||||||
|
// val is treated as a 4-bit value
|
||||||
|
static inline void u4_arr_set(u8 *arr, size_t idx, u8 val) {
|
||||||
|
size_t off = idx >> 1;
|
||||||
|
size_t shift = (idx & 1) << 2;
|
||||||
|
u8 mask = (u8) (0xf0 >> shift);
|
||||||
|
arr[off] = (arr[off] & mask) | (val << shift);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u8 u4_arr_get(const u8 *arr, size_t idx) {
|
||||||
|
size_t off = idx >> 1;
|
||||||
|
size_t shift = (idx & 1) << 2;
|
||||||
|
return (u8) ((arr[off] >> shift) & 0xf);
|
||||||
|
}
|
||||||
|
|
||||||
|
COLD noreturn void fatal_error(const char *s);
|
||||||
|
|
||||||
|
#if CONFIG_SEAL_METADATA
|
||||||
|
|
||||||
|
#ifdef __GLIBC__
|
||||||
|
#define USE_PKEY
|
||||||
|
#else
|
||||||
|
#error "CONFIG_SEAL_METADATA requires Memory Protection Key support"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // CONFIG_SEAL_METADATA
|
||||||
|
|
||||||
|
#endif
|
78
src/lib.rs
Normal file
78
src/lib.rs
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
use core::ffi::{c_void, c_int};
|
||||||
|
|
||||||
|
extern crate libc;
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
/*
|
||||||
|
TODO: implement this
|
||||||
|
|
||||||
|
#ifdef __ANDROID__
|
||||||
|
#define H_MALLOC_USABLE_SIZE_CONST const
|
||||||
|
#else
|
||||||
|
#define H_MALLOC_USABLE_SIZE_CONST
|
||||||
|
#endif
|
||||||
|
|
||||||
|
for:
|
||||||
|
// glibc extensions
|
||||||
|
size_t h_malloc_usable_size(H_MALLOC_USABLE_SIZE_CONST void *ptr);
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* C standard */
|
||||||
|
|
||||||
|
pub fn h_malloc(size: usize) -> *mut c_void;
|
||||||
|
pub fn h_calloc(nmemb: usize, size: usize) -> *mut c_void;
|
||||||
|
pub fn h_realloc(ptr: *mut c_void, size: usize) -> *mut c_void;
|
||||||
|
pub fn h_aligned_malloc(alignment: usize, size: usize) -> *mut c_void;
|
||||||
|
pub fn h_free(ptr: *mut c_void);
|
||||||
|
|
||||||
|
/* POSIX */
|
||||||
|
|
||||||
|
pub fn h_posix_memalign(memptr: *mut *mut c_void, alignment: usize, size: usize) -> c_int;
|
||||||
|
|
||||||
|
/* glibc extensions */
|
||||||
|
|
||||||
|
pub fn h_malloc_usable_size(ptr: *const c_void) -> usize;
|
||||||
|
pub fn h_mallopt(param: c_int, value: c_int) -> c_int;
|
||||||
|
pub fn h_malloc_trim(pad: usize) -> c_int;
|
||||||
|
pub fn h_malloc_stats(void: c_void) -> c_void;
|
||||||
|
|
||||||
|
/* obsolete glibc extensions */
|
||||||
|
|
||||||
|
pub fn h_memalign(alignment: usize, size: usize) -> *mut c_void;
|
||||||
|
pub fn h_pvalloc(size: usize) -> *mut c_void;
|
||||||
|
pub fn h_cfree(ptr: *mut c_void) -> c_void;
|
||||||
|
pub fn h_malloc_get_state(void: c_void) -> c_void;
|
||||||
|
pub fn h_malloc_set_state(ptr: *mut c_void) -> c_int;
|
||||||
|
|
||||||
|
/*TODO: implement this see the top:
|
||||||
|
#if defined(__GLIBC__) || defined(__ANDROID__)
|
||||||
|
struct mallinfo h_mallinfo(void);
|
||||||
|
#endif
|
||||||
|
#ifndef __ANDROID__
|
||||||
|
int h_malloc_info(int options, FILE *fp);
|
||||||
|
#endif
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* hardened_malloc extensions */
|
||||||
|
|
||||||
|
/// return an upper bound on object size for any pointer based on malloc metadata
|
||||||
|
pub fn h_malloc_object_size(ptr: *const c_void) -> usize;
|
||||||
|
|
||||||
|
/// similar to malloc_object_size, but avoids locking so the results are much more limited
|
||||||
|
pub fn h_malloc_object_size_fast(ptr: *const c_void) -> usize;
|
||||||
|
|
||||||
|
|
||||||
|
/// The free function with an extra parameter for passing the size requested at
|
||||||
|
/// allocation time.
|
||||||
|
///
|
||||||
|
/// This offers the same functionality as C++14 sized deallocation and can be
|
||||||
|
/// used to implement it.
|
||||||
|
///
|
||||||
|
/// A performance-oriented allocator would use this as a performance
|
||||||
|
/// enhancement with undefined behavior on a mismatch. Instead, this hardened
|
||||||
|
/// allocator implementation uses it to improve security by checking that the
|
||||||
|
/// passed size matches the allocated size.
|
||||||
|
pub fn h_free_sized(ptr: *mut c_void, expected_size: usize) -> c_void;
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue