From a866cfcbd0622e7156146b826499bf5637458fbe Mon Sep 17 00:00:00 2001 From: Tom Linton Date: Wed, 16 Feb 2022 14:08:14 +1300 Subject: [PATCH 1/5] Add section on safety checks --- src/chapter_3/errors.md | 47 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 43 insertions(+), 4 deletions(-) diff --git a/src/chapter_3/errors.md b/src/chapter_3/errors.md index 56a9727..39df73e 100644 --- a/src/chapter_3/errors.md +++ b/src/chapter_3/errors.md @@ -1,5 +1,6 @@ # Errors -There are three types of errors in anchor programs. Anchor Internal Errors, Custom Errors, and non-anchor errors. + +There are three types of errors in Anchor programs. Anchor Internal Errors, Custom Errors, and non-Anchor errors. Additionally, Anchor can produce safety check errors during program compilation. The autogenerated clients can automatically parse Anchor Internal Errors and Custom Errors so they can display the error code and error message. This is not possible for non-anchor errors where clients just return the raw error returned by the underlying solana client libraries. @@ -13,7 +14,7 @@ Anchor has many different internal error codes. These are not meant to be used b ## Custom Errors -You can add errors that are unique to your program by using the error attribute. +You can add errors that are unique to your program by using the error attribute. Simply add it to an enum with a name of your choice. You can then use the variants of the enum as errors in your program. Additionally, you can add a message attribute to the individual variants. Clients will then display this error message if the error occurs. Custom Error code numbers start at the [custom error offset](https://docs.rs/anchor-lang/latest/anchor_lang/__private/constant.ERROR_CODE_OFFSET.html). @@ -23,7 +24,7 @@ mod hello_anchor { use super::*; pub fn set_data(ctx: Context, data: MyAccount) -> ProgramResult { if data.data >= 100 { - return Err(MyError::DataTooLarge.into()); + return Err(MyError::DataTooLarge.into()); } ctx.accounts.my_account.set_inner(data); Ok(()) @@ -39,12 +40,13 @@ pub enum MyError { ``` You can use the [`require`](https://docs.rs/anchor-lang/latest/anchor_lang/macro.require.html) macro to simplify writing errors. The code above can be simplified to this (Note that the `>=` flips to `<`): + ```rust,ignore #[program] mod hello_anchor { use super::*; pub fn set_data(ctx: Context, data: MyAccount) -> ProgramResult { - require!(data.data < 100, MyError::DataTooLarge); + require!(data.data < 100, MyError::DataTooLarge); ctx.accounts.my_account.set_inner(data); Ok(()) } @@ -57,3 +59,40 @@ pub enum MyError { DataTooLarge } ``` + +## Safety checks + +Many of the [common security pitfalls](https://blog.neodyme.io/posts/solana_common_pitfalls) in Solana programs stem from the provision of arbitrary accounts to program functions. Checking the owner of an account, signer(s) of an instruction, or the account data type are examples. Anchor encourages the use of [account types](http://localhost:3000/chapter_3/the_accounts_struct.html#the-account-type) that implement these checks automatically. + +Two of the Anchor account types, [AccountInfo](https://docs.rs/anchor-lang/latest/anchor_lang/accounts/account_info/index.html) and [UncheckedAccount](https://docs.rs/anchor-lang/latest/anchor_lang/accounts/unchecked_account/index.html) do not implement any checks on the account being passed. Anchor implements safety checks that encourage additional documentation of potential security risks if you use either of these types. + +Attempting to build a program containing the following excerpt with `anchor build`: + +```rust,ignore +#[derive(Accounts)] +pub struct Initialize<'info> { + pub potentially_dangerous: UncheckedAccount<'info> +} +``` + +will result in an error similar to the following: + +``` +Error: + /anchor/tests/unchecked/programs/unchecked/src/lib.rs:15:8 + Struct field "potentially_dangerous" is unsafe, but is not documented. + Please add a `/// SAFETY:` doc comment to the field enumerating potential security risks. + See https://book.anchor-lang.com/chapter_3/errors.html#safety-checks for more information. +``` + +To fix this, write a doc comment describing the potential security implications, e.g.: + +```rust,ignore +#[derive(Accounts)] +pub struct Initialize<'info> { + /// SAFETY: This is not dangerous because we don't read or write from this account + pub potentially_dangerous: UncheckedAccount<'info> +} +``` + +Note the doc comment needs to be a [line or block doc comment](https://doc.rust-lang.org/reference/comments.html#doc-comments) (/// or /\*\*) to be interepreted as doc attribute by Rust. Double slash comments (//) are not interpreted as such. From cd9eb64e9645b7672b1baa4b7fdf7279be30c819 Mon Sep 17 00:00:00 2001 From: Tom Linton Date: Thu, 17 Feb 2022 16:25:50 +1300 Subject: [PATCH 2/5] Update error, document exceptions --- src/chapter_3/errors.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/chapter_3/errors.md b/src/chapter_3/errors.md index 39df73e..bc3bcf3 100644 --- a/src/chapter_3/errors.md +++ b/src/chapter_3/errors.md @@ -64,7 +64,7 @@ pub enum MyError { Many of the [common security pitfalls](https://blog.neodyme.io/posts/solana_common_pitfalls) in Solana programs stem from the provision of arbitrary accounts to program functions. Checking the owner of an account, signer(s) of an instruction, or the account data type are examples. Anchor encourages the use of [account types](http://localhost:3000/chapter_3/the_accounts_struct.html#the-account-type) that implement these checks automatically. -Two of the Anchor account types, [AccountInfo](https://docs.rs/anchor-lang/latest/anchor_lang/accounts/account_info/index.html) and [UncheckedAccount](https://docs.rs/anchor-lang/latest/anchor_lang/accounts/unchecked_account/index.html) do not implement any checks on the account being passed. Anchor implements safety checks that encourage additional documentation of potential security risks if you use either of these types. +Two of the Anchor account types, [AccountInfo](https://docs.rs/anchor-lang/latest/anchor_lang/accounts/account_info/index.html) and [UncheckedAccount](https://docs.rs/anchor-lang/latest/anchor_lang/accounts/unchecked_account/index.html) do not implement any checks on the account being passed. Anchor implements safety checks that encourage additional documentation of potential security risks if you use either of these types. The exception to this is if the account is being initialized or if it is a program derived address. These are determined by the presence of `init` or `seeds` constraints in the account attribute for the account. Attempting to build a program containing the following excerpt with `anchor build`: @@ -81,7 +81,7 @@ will result in an error similar to the following: Error: /anchor/tests/unchecked/programs/unchecked/src/lib.rs:15:8 Struct field "potentially_dangerous" is unsafe, but is not documented. - Please add a `/// SAFETY:` doc comment to the field enumerating potential security risks. + Please add a `/// SAFETY:` doc comment explaining why no checks through types are necessary. See https://book.anchor-lang.com/chapter_3/errors.html#safety-checks for more information. ``` From f05db1b57291654b693c8cfd8a15d9d819d76ebf Mon Sep 17 00:00:00 2001 From: Tom Linton Date: Thu, 17 Feb 2022 21:40:10 +1300 Subject: [PATCH 3/5] Move to struct section --- src/chapter_3/errors.md | 47 +++------------------------- src/chapter_3/the_accounts_struct.md | 40 ++++++++++++++++++++++- 2 files changed, 43 insertions(+), 44 deletions(-) diff --git a/src/chapter_3/errors.md b/src/chapter_3/errors.md index bc3bcf3..56a9727 100644 --- a/src/chapter_3/errors.md +++ b/src/chapter_3/errors.md @@ -1,6 +1,5 @@ # Errors - -There are three types of errors in Anchor programs. Anchor Internal Errors, Custom Errors, and non-Anchor errors. Additionally, Anchor can produce safety check errors during program compilation. +There are three types of errors in anchor programs. Anchor Internal Errors, Custom Errors, and non-anchor errors. The autogenerated clients can automatically parse Anchor Internal Errors and Custom Errors so they can display the error code and error message. This is not possible for non-anchor errors where clients just return the raw error returned by the underlying solana client libraries. @@ -14,7 +13,7 @@ Anchor has many different internal error codes. These are not meant to be used b ## Custom Errors -You can add errors that are unique to your program by using the error attribute. +You can add errors that are unique to your program by using the error attribute. Simply add it to an enum with a name of your choice. You can then use the variants of the enum as errors in your program. Additionally, you can add a message attribute to the individual variants. Clients will then display this error message if the error occurs. Custom Error code numbers start at the [custom error offset](https://docs.rs/anchor-lang/latest/anchor_lang/__private/constant.ERROR_CODE_OFFSET.html). @@ -24,7 +23,7 @@ mod hello_anchor { use super::*; pub fn set_data(ctx: Context, data: MyAccount) -> ProgramResult { if data.data >= 100 { - return Err(MyError::DataTooLarge.into()); + return Err(MyError::DataTooLarge.into()); } ctx.accounts.my_account.set_inner(data); Ok(()) @@ -40,13 +39,12 @@ pub enum MyError { ``` You can use the [`require`](https://docs.rs/anchor-lang/latest/anchor_lang/macro.require.html) macro to simplify writing errors. The code above can be simplified to this (Note that the `>=` flips to `<`): - ```rust,ignore #[program] mod hello_anchor { use super::*; pub fn set_data(ctx: Context, data: MyAccount) -> ProgramResult { - require!(data.data < 100, MyError::DataTooLarge); + require!(data.data < 100, MyError::DataTooLarge); ctx.accounts.my_account.set_inner(data); Ok(()) } @@ -59,40 +57,3 @@ pub enum MyError { DataTooLarge } ``` - -## Safety checks - -Many of the [common security pitfalls](https://blog.neodyme.io/posts/solana_common_pitfalls) in Solana programs stem from the provision of arbitrary accounts to program functions. Checking the owner of an account, signer(s) of an instruction, or the account data type are examples. Anchor encourages the use of [account types](http://localhost:3000/chapter_3/the_accounts_struct.html#the-account-type) that implement these checks automatically. - -Two of the Anchor account types, [AccountInfo](https://docs.rs/anchor-lang/latest/anchor_lang/accounts/account_info/index.html) and [UncheckedAccount](https://docs.rs/anchor-lang/latest/anchor_lang/accounts/unchecked_account/index.html) do not implement any checks on the account being passed. Anchor implements safety checks that encourage additional documentation of potential security risks if you use either of these types. The exception to this is if the account is being initialized or if it is a program derived address. These are determined by the presence of `init` or `seeds` constraints in the account attribute for the account. - -Attempting to build a program containing the following excerpt with `anchor build`: - -```rust,ignore -#[derive(Accounts)] -pub struct Initialize<'info> { - pub potentially_dangerous: UncheckedAccount<'info> -} -``` - -will result in an error similar to the following: - -``` -Error: - /anchor/tests/unchecked/programs/unchecked/src/lib.rs:15:8 - Struct field "potentially_dangerous" is unsafe, but is not documented. - Please add a `/// SAFETY:` doc comment explaining why no checks through types are necessary. - See https://book.anchor-lang.com/chapter_3/errors.html#safety-checks for more information. -``` - -To fix this, write a doc comment describing the potential security implications, e.g.: - -```rust,ignore -#[derive(Accounts)] -pub struct Initialize<'info> { - /// SAFETY: This is not dangerous because we don't read or write from this account - pub potentially_dangerous: UncheckedAccount<'info> -} -``` - -Note the doc comment needs to be a [line or block doc comment](https://doc.rust-lang.org/reference/comments.html#doc-comments) (/// or /\*\*) to be interepreted as doc attribute by Rust. Double slash comments (//) are not interpreted as such. diff --git a/src/chapter_3/the_accounts_struct.md b/src/chapter_3/the_accounts_struct.md index 6b83ab7..e1f5ff2 100644 --- a/src/chapter_3/the_accounts_struct.md +++ b/src/chapter_3/the_accounts_struct.md @@ -124,4 +124,42 @@ pub struct SetData<'info> { } ``` -You can find information about all constraints in the reference. We will cover some of the most important ones in the milestone project at the end of the Essentials section. \ No newline at end of file +You can find information about all constraints in the reference. We will cover some of the most important ones in the milestone project at the end of the Essentials section. + +## Safety checks + +Many of the [common security pitfalls](https://blog.neodyme.io/posts/solana_common_pitfalls) in Solana programs stem from the provision of arbitrary accounts to program functions. Checking the owner of an account, signer(s) of an instruction, or the account data type are examples. Anchor encourages checks through the use of the appropriate account types. + +Two of the Anchor account types, [AccountInfo](https://docs.rs/anchor-lang/latest/anchor_lang/accounts/account_info/index.html) and [UncheckedAccount](https://docs.rs/anchor-lang/latest/anchor_lang/accounts/unchecked_account/index.html) do not implement any checks on the account being passed. Anchor implements safety checks that encourage additional documentation describing why additional checks are not necesssary. The exception to this is if the account is being initialized or if it is a program derived address. These are determined by the presence of `init` or `seeds` constraints in the account attribute for the account. + +Attempting to build a program containing the following excerpt with `anchor build`: + +```rust,ignore +#[derive(Accounts)] +pub struct Initialize<'info> { + pub potentially_dangerous: UncheckedAccount<'info> +} +``` + +will result in an error similar to the following: + +``` +Error: + /anchor/tests/unchecked/programs/unchecked/src/lib.rs:15:8 + Struct field "potentially_dangerous" is unsafe, but is not documented. + Please add a `/// SAFETY:` doc comment explaining why no checks through types are necessary. + See https://book.anchor-lang.com/chapter_3/the_accounts_struct.html#safety-checks for more information. +``` + +To fix this, write a doc comment describing the potential security implications, e.g.: + +```rust,ignore +#[derive(Accounts)] +pub struct Initialize<'info> { + /// SAFETY: This is not dangerous because we don't read or write from this account + pub potentially_dangerous: UncheckedAccount<'info> +} +``` + +Note the doc comment needs to be a [line or block doc comment](https://doc.rust-lang.org/reference/comments.html#doc-comments) (/// or /\*\*) to be interepreted as doc attribute by Rust. Double slash comments (//) are not interpreted as such. + From ad5373539b04334abe69104761213945dccef580 Mon Sep 17 00:00:00 2001 From: Tom Linton Date: Fri, 18 Feb 2022 10:11:18 +1300 Subject: [PATCH 4/5] Remove exception statement --- src/chapter_3/the_accounts_struct.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/chapter_3/the_accounts_struct.md b/src/chapter_3/the_accounts_struct.md index e1f5ff2..4db9160 100644 --- a/src/chapter_3/the_accounts_struct.md +++ b/src/chapter_3/the_accounts_struct.md @@ -1,11 +1,12 @@ # The Accounts Struct -The Accounts struct is where you define which accounts your instruction expects and which constraints these accounts should adhere to. You do this via two constructs: Types and constraints. + +The Accounts struct is where you define which accounts your instruction expects and which constraints these accounts should adhere to. You do this via two constructs: Types and constraints. ## Types > [Account Types Reference](https://docs.rs/anchor-lang/latest/anchor_lang/accounts/index.html) -Each type has a specific use case in mind. Detailed explanations for the types can be found in the [reference](https://docs.rs/anchor-lang/latest/anchor_lang/accounts/index.html). We will briefly explain the most important type here, the `Account` type. +Each type has a specific use case in mind. Detailed explanations for the types can be found in the [reference](https://docs.rs/anchor-lang/latest/anchor_lang/accounts/index.html). We will briefly explain the most important type here, the `Account` type. ### The Account Type @@ -46,8 +47,7 @@ Most importantly, the `#[account]` attribute sets the owner of that data to the #### Using `Account<'a, T>` with non-anchor program accounts - -There may be cases where you want your program to interact with a non-Anchor program. You can still get all the benefits of `Account` but you have to write a custom wrapper type instead of using `#[account]`. For instance, Anchor provides wrapper types for the token program accounts so they can be used with `Account`. +There may be cases where you want your program to interact with a non-Anchor program. You can still get all the benefits of `Account` but you have to write a custom wrapper type instead of using `#[account]`. For instance, Anchor provides wrapper types for the token program accounts so they can be used with `Account`. ```rust,ignore use anchor_lang::prelude::*; @@ -97,12 +97,14 @@ Check out the [reference for the Account type](https://docs.rs/anchor-lang/lates Account types can do a lot of work for you but they're not dynamic enough to handle all the security checks a secure program requires. Add constraints to an account with the following format: + ```rust,ignore #[account()] pub account: AccountType ``` Some constraints support custom Errors (we will explore errors [later](./errors.md)): + ```rust,ignore #[account(..., @ MyError::MyErrorVariant, ...)] pub account: AccountType @@ -130,7 +132,7 @@ You can find information about all constraints in the reference. We will cover s Many of the [common security pitfalls](https://blog.neodyme.io/posts/solana_common_pitfalls) in Solana programs stem from the provision of arbitrary accounts to program functions. Checking the owner of an account, signer(s) of an instruction, or the account data type are examples. Anchor encourages checks through the use of the appropriate account types. -Two of the Anchor account types, [AccountInfo](https://docs.rs/anchor-lang/latest/anchor_lang/accounts/account_info/index.html) and [UncheckedAccount](https://docs.rs/anchor-lang/latest/anchor_lang/accounts/unchecked_account/index.html) do not implement any checks on the account being passed. Anchor implements safety checks that encourage additional documentation describing why additional checks are not necesssary. The exception to this is if the account is being initialized or if it is a program derived address. These are determined by the presence of `init` or `seeds` constraints in the account attribute for the account. +Two of the Anchor account types, [AccountInfo](https://docs.rs/anchor-lang/latest/anchor_lang/accounts/account_info/index.html) and [UncheckedAccount](https://docs.rs/anchor-lang/latest/anchor_lang/accounts/unchecked_account/index.html) do not implement any checks on the account being passed. Anchor implements safety checks that encourage additional documentation describing why additional checks are not necesssary. Attempting to build a program containing the following excerpt with `anchor build`: @@ -162,4 +164,3 @@ pub struct Initialize<'info> { ``` Note the doc comment needs to be a [line or block doc comment](https://doc.rust-lang.org/reference/comments.html#doc-comments) (/// or /\*\*) to be interepreted as doc attribute by Rust. Double slash comments (//) are not interpreted as such. - From 267562f247bb1ea7903956496ac38b0f461a7228 Mon Sep 17 00:00:00 2001 From: Tom Linton Date: Fri, 18 Feb 2022 12:43:03 +1300 Subject: [PATCH 5/5] Remove redundant paragraph --- src/chapter_3/the_accounts_struct.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/chapter_3/the_accounts_struct.md b/src/chapter_3/the_accounts_struct.md index 4db9160..e0a9d08 100644 --- a/src/chapter_3/the_accounts_struct.md +++ b/src/chapter_3/the_accounts_struct.md @@ -130,8 +130,6 @@ You can find information about all constraints in the reference. We will cover s ## Safety checks -Many of the [common security pitfalls](https://blog.neodyme.io/posts/solana_common_pitfalls) in Solana programs stem from the provision of arbitrary accounts to program functions. Checking the owner of an account, signer(s) of an instruction, or the account data type are examples. Anchor encourages checks through the use of the appropriate account types. - Two of the Anchor account types, [AccountInfo](https://docs.rs/anchor-lang/latest/anchor_lang/accounts/account_info/index.html) and [UncheckedAccount](https://docs.rs/anchor-lang/latest/anchor_lang/accounts/unchecked_account/index.html) do not implement any checks on the account being passed. Anchor implements safety checks that encourage additional documentation describing why additional checks are not necesssary. Attempting to build a program containing the following excerpt with `anchor build`: