From 49c7c7e06e747e565c957083e8e7f4c0b6f95371 Mon Sep 17 00:00:00 2001 From: Corban Villa Date: Sat, 10 Feb 2024 12:52:04 +0000 Subject: [PATCH 1/6] begin asan sanitizer work --- examples/buffer_overflow.st | 14 ++++++++++++++ src/codegen.rs | 37 +++++++++++++++++++++++++++++++++++-- 2 files changed, 49 insertions(+), 2 deletions(-) create mode 100644 examples/buffer_overflow.st diff --git a/examples/buffer_overflow.st b/examples/buffer_overflow.st new file mode 100644 index 0000000000..152ad4ae03 --- /dev/null +++ b/examples/buffer_overflow.st @@ -0,0 +1,14 @@ +FUNCTION main : DINT + buf_overflow(); +END_FUNCTION + +FUNCTION buf_overflow : DINT +VAR + i : DINT; + buf : ARRAY [0..2] OF BYTE; +END_VAR + +FOR i := 0 TO 3 DO + buf[i] := 1; +END_FOR +END_FUNCTION \ No newline at end of file diff --git a/src/codegen.rs b/src/codegen.rs index 12918eeda8..f2dfca4ab1 100644 --- a/src/codegen.rs +++ b/src/codegen.rs @@ -25,6 +25,7 @@ use crate::{ use super::index::*; use indexmap::IndexSet; use inkwell::{ + attributes::{Attribute, AttributeLoc}, context::Context, execution_engine::{ExecutionEngine, JitFunction}, memory_buffer::MemoryBuffer, @@ -195,6 +196,26 @@ impl<'ink> CodeGen<'ink> { global_index: &Index, llvm_index: &LlvmTypedIndex, ) -> Result, Diagnostic> { + // This loops through functions and adds the `sanitize_address` attribute. + // The AddressSanitizer LLVM passes will then use this to identify which + // functions need to be instrumented. + // + // There's likely somewhere else this should go. Placing it here for now. + // TODO: Once it's in it's proper place, we'll add a flag to enable/disable it. + for implementation in &unit.implementations { + // Skip non-internal functions (external links + built-ins) + if implementation.linkage != LinkageType::Internal { + continue; + } + + let func = llvm_index + .find_associated_implementation(&implementation.name) + .expect("Unable to get function definition!"); + let sanitizer_attribute_id = Attribute::get_named_enum_kind_id("sanitize_address"); + let sanitizer_attribute = context.create_enum_attribute(sanitizer_attribute_id, 0); + func.add_attribute(AttributeLoc::Function, sanitizer_attribute); + } + //generate all pous let llvm = Llvm::new(context, context.create_builder()); let pou_generator = PouGenerator::new(llvm, global_index, annotations, llvm_index); @@ -330,11 +351,23 @@ impl<'ink> GeneratedModule<'ink> { if let Some(parent) = output.parent() { std::fs::create_dir_all(parent)?; } - ////Run the passes + // Run the passes + // TODO - Add asan as optional cli parameter + // TODO - Currently doesn't add linking parameters for the asan runtime + // + // NOTE - these pass names have changed in newer versions of LLVM. + // - [Latest](https://github.com/llvm/llvm-project/blob/main/llvm/lib/Passes/PassRegistry.def) + // - [LLVM 14](https://github.com/llvm/llvm-project/blob/release/14.x/llvm/lib/Passes/PassRegistry.def) + // It should also be noted that the ASAN pass requies: + // - Specific target architectures [see here](https://github.com/google/sanitizers/wiki/AddressSanitizer). + // - Output is NOT IR (otherwise the pass will run twice with the next compiler (i.e. clang) and it will cause a bad time) + + let passes = format!("{},asan-module,function(asan)", optimization_level.opt_params()); + machine .and_then(|it| { self.module - .run_passes(optimization_level.opt_params(), &it, PassBuilderOptions::create()) + .run_passes(&passes, &it, PassBuilderOptions::create()) .map_err(|it| { Diagnostic::llvm_error(output.to_str().unwrap_or_default(), &it.to_string_lossy()) }) From d33acf900a819bf7bba6ec7640442f2e37c86c1e Mon Sep 17 00:00:00 2001 From: Corban Villa Date: Sat, 10 Feb 2024 14:16:36 +0000 Subject: [PATCH 2/6] fix undefined functions --- src/codegen.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/codegen.rs b/src/codegen.rs index f2dfca4ab1..3817e42bae 100644 --- a/src/codegen.rs +++ b/src/codegen.rs @@ -208,6 +208,14 @@ impl<'ink> CodeGen<'ink> { continue; } + // Skip no-definition functions + // TODO - investigate which functions don't have definitions and why + // TODO - better way to log this than println + if module.get_function(&implementation.name).is_none() { + println!("Skipping undefined function: {}", &implementation.name); + continue; + } + let func = llvm_index .find_associated_implementation(&implementation.name) .expect("Unable to get function definition!"); From 051e2636749c2e15fd32dfb4f8fdfd6f3aa616ce Mon Sep 17 00:00:00 2001 From: Corban Villa Date: Sat, 10 Feb 2024 14:25:22 +0000 Subject: [PATCH 3/6] fix lookup --- src/codegen.rs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/codegen.rs b/src/codegen.rs index 3817e42bae..b77d3029ed 100644 --- a/src/codegen.rs +++ b/src/codegen.rs @@ -211,14 +211,13 @@ impl<'ink> CodeGen<'ink> { // Skip no-definition functions // TODO - investigate which functions don't have definitions and why // TODO - better way to log this than println - if module.get_function(&implementation.name).is_none() { - println!("Skipping undefined function: {}", &implementation.name); - continue; - } - - let func = llvm_index - .find_associated_implementation(&implementation.name) - .expect("Unable to get function definition!"); + let func = match llvm_index.find_associated_implementation(&implementation.name) { + Some(func) => func, + None => { + println!("Skipping undefined function: {}", &implementation.name); + continue; + } + }; let sanitizer_attribute_id = Attribute::get_named_enum_kind_id("sanitize_address"); let sanitizer_attribute = context.create_enum_attribute(sanitizer_attribute_id, 0); func.add_attribute(AttributeLoc::Function, sanitizer_attribute); From 71dd3a5981457883ba26767121901245a1523c97 Mon Sep 17 00:00:00 2001 From: Corban Villa Date: Fri, 24 May 2024 10:23:35 +0000 Subject: [PATCH 4/6] enable build --- scripts/build.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/build.sh b/scripts/build.sh index 8d21a54183..092f1a04ac 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -5,7 +5,7 @@ vendor=0 offline=0 check=0 check_style=0 -build=0 +build=1 doc=0 test=0 coverage=0 @@ -127,7 +127,7 @@ function run_doc() { log "Building book" log "Building preprocessor for the book" cargo build --release -p errorcode_book_generator - cd book && mdbook build + cd book && mdbook build # test is disabled because not all files in the book exist. The pre-processor for error codes adds new files # mdbook test } From 7342af19ee5714b8efddbcd56e9e31dfcbc94789 Mon Sep 17 00:00:00 2001 From: Corban Villa Date: Wed, 29 May 2024 11:37:08 +0000 Subject: [PATCH 5/6] docker image workflow --- .dockerignore | 21 ++++++++++++ .github/workflows/docker.yml | 65 ++++++++++++++++++++++++++++++++++++ Dockerfile | 49 +++++++++++++++++++++++++++ 3 files changed, 135 insertions(+) create mode 100644 .dockerignore create mode 100644 .github/workflows/docker.yml create mode 100644 Dockerfile diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000000..f245c19dcf --- /dev/null +++ b/.dockerignore @@ -0,0 +1,21 @@ +# Ignore target directory +target/ + +# Ignore any build artifacts +**/*.rs.bk + +# Ignore any generated files +**/Cargo.lock +**/Cargo.toml.orig +**/Cargo.toml.bk + +# Ignore any IDE-specific files +.vscode/ +.idea/ + +# Ignore Dockerfile and dockerignore file +Dockerfile +.dockerignore + +# Workflow +.github \ No newline at end of file diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml new file mode 100644 index 0000000000..73c251aca1 --- /dev/null +++ b/.github/workflows/docker.yml @@ -0,0 +1,65 @@ +name: Docker Image CI + +on: + push: + branches: [ '*' ] + +env: + IMAGE_NAME: rusty + +jobs: + # Push image to GitHub Packages. + # See also https://docs.docker.com/docker-hub/builds/ + build-linux: + runs-on: ${{ matrix.config.os }} + strategy: + matrix: + config: + - { + os: "ubuntu-latest", + version: "linux", + arch: "x86_64" + } + permissions: + packages: write + contents: read + + steps: + - uses: actions/checkout@v3 + + - name: Build image + working-directory: ${{ matrix.config.version }} + shell: bash + run: docker buildx build . --platform ${{matrix.config.version}}/${{matrix.config.arch}} --file Dockerfile --tag $IMAGE_NAME + + - name: Log in to registry + if: ${{ github.event_name != 'pull_request' }} + # This is where you will update the PAT to GITHUB_TOKEN + run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin + + - name: Push image + shell: bash + if: ${{ github.event_name != 'pull_request' }} + run: | + IMAGE_ID=ghcr.io/${{ github.repository_owner }}/$IMAGE_NAME + + # Extract branch name + BRANCH_NAME=$(echo "${{ github.ref }}" | sed -e 's,.*/\(.*\),\1,') + + # Append branch name to image ID + IMAGE_ID=$IMAGE_ID-$BRANCH_NAME + + # Change all uppercase to lowercase + IMAGE_ID=$(echo $IMAGE_ID | tr '[A-Z]' '[a-z]') + + # Strip git ref prefix from version + VERSION=$(echo "${{ github.ref }}" | sed -e 's,.*/\(.*\),\1,') + + # Use Docker `latest` tag convention + [ "$VERSION" == "main" ] && VERSION=latest + #Add the platform to the version + VERSION=$VERSION-${{ matrix.config.arch }} + echo IMAGE_ID=$IMAGE_ID + echo VERSION=$VERSION + docker tag $IMAGE_NAME $IMAGE_ID:$VERSION + docker push $IMAGE_ID:$VERSION diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000000..2d1665b7c6 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,49 @@ +# FROM mcr.microsoft.com/vscode/devcontainers/rust:latest +FROM ghcr.io/plc-lang/rust-llvm:latest + +# Avoid warnings by switching to noninteractive +ENV DEBIAN_FRONTEND=noninteractive + +# This Dockerfile adds a non-root user with sudo access. Use the "remoteUser" +# property in devcontainer.json to use it. On Linux, the container user's GID/UIDs +# will be updated to match your local UID/GID (when using the dockerFile property). +# See https://aka.ms/vscode-remote/containers/non-root-user for details. +# ARG USERNAME=vscode +# ARG USER_UID=1000 +# ARG USER_GID=$USER_UID + +# # Create a non-root user to use if preferred - see https://aka.ms/vscode-remote/containers/non-root-user. +# RUN groupadd --gid $USER_GID $USERNAME \ +# && useradd -s /bin/bash --uid $USER_UID --gid $USER_GID -m $USERNAME \ +# # [Optional] Add sudo support for the non-root user +# && apt-get install -y sudo \ +# && echo $USERNAME ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/$USERNAME\ +# && chmod 0440 /etc/sudoers.d/$USERNAME + +# RUN apt-get -y update +# RUN apt-get -y install git gdb docker.io + +# RUN cargo install cargo-insta cargo-watch + +# Give all users access to cargo and rust home +RUN chmod -R a+rw $CARGO_HOME \ + && chmod -R a+rw $RUSTUP_HOME + +# Switch back to dialog for any ad-hoc use of apt-get +ENV DEBIAN_FRONTEND=dialog +ENV LLVM_VER=14 + +# Required if we want to use `lld` as the default linker for RuSTy +RUN ln -sf /usr/bin/ld.lld-$LLVM_VER /usr/bin/ld.lld + +# Install the local RuSTy version +WORKDIR /rusty +COPY . . +RUN sed -i 's/build=0/build=1/' ./scripts/build.sh && \ + ./scripts/build.sh + +# Allow invoking `plc` from anywhere +ENV PATH="/rusty/target/debug:${PATH}" + +ENTRYPOINT [ "/bin/bash", "-c" ] +CMD ["plc", "--help"] From cec8a5829190139dd6c5ca30df4bf91826363b01 Mon Sep 17 00:00:00 2001 From: Corban Villa Date: Wed, 29 May 2024 11:43:07 +0000 Subject: [PATCH 6/6] fix workflow --- .github/workflows/docker.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 73c251aca1..5585960b80 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -28,7 +28,6 @@ jobs: - uses: actions/checkout@v3 - name: Build image - working-directory: ${{ matrix.config.version }} shell: bash run: docker buildx build . --platform ${{matrix.config.version}}/${{matrix.config.arch}} --file Dockerfile --tag $IMAGE_NAME