Dynamic Array Stack Corruption - ethereum

This contract appears to be a game offering 1/16 odds at the balance of the contract. However, when running the code in a debugger it appears as if the 'secretNumber' variable is being overwritten before it is used.
pragma solidity ^0.4.19;
contract CryptoRoulette {
uint256 private secretNumber;
uint256 public lastPlayed;
uint256 public betPrice = 0.1 ether;
address public ownerAddr;
struct Game {
address player;
uint256 number;
}
Game[] public gamesPlayed;
function CryptoRoulette() public {
ownerAddr = msg.sender;
shuffle();
}
function shuffle() internal {
// initialize secretNumber with a value between 0 and 15
secretNumber = uint8(sha3(now, block.blockhash(block.number-1))) % 16;
}
function play(uint256 number) payable public {
require(msg.value >= betPrice && number < 16);
Game game;
game.player = msg.sender;
game.number = number;
gamesPlayed.push(game);
if (number == secretNumber) {
// win!
msg.sender.transfer(this.balance);
}
shuffle();
lastPlayed = now;
}
function kill() public {
if (msg.sender == ownerAddr && now > lastPlayed + 1 days) {
suicide(msg.sender);
}
}
function() public payable { }
}
The way secretNumber is updated, it should always be less than 16
secretNumber = uint8(sha3(now, block.blockhash(block.number-1))) % 16;
This debugger output shows that during execution of if (number == secretNumber) { the value of secretNumber has been updated to, oddly enough, the callers address (msg.sender).
`
(243) PUSH1 0x00
000000000000000000000000000000000000000000000000000000006898f82b
0000000000000000000000000000000000000000000000000000000000000143
0000000000000000000000000000000000000000000000000000000000000003
0000000000000000000000000000000000000000000000000000000000000000 (top)
40: if (number == secretNumber) {
^^^^^^^^^^^^
debug(develop:0x98cacf83...)> i
CryptoRoulette.sol | 0xbd2c938b9f6bfc1a66368d08cb44dc3eb2ae27be:
40: if (number == secretNumber) {
^^^^^^
debug(develop:0x98cacf83...)> p
CryptoRoulette.sol | 0xbd2c938b9f6bfc1a66368d08cb44dc3eb2ae27be:
(245) DUP3
000000000000000000000000000000000000000000000000000000006898f82b
0000000000000000000000000000000000000000000000000000000000000143
0000000000000000000000000000000000000000000000000000000000000003
0000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000627306090abab3a6e1400e9345bc60c78a8bef57 (top)
40: if (number == secretNumber) {
^^^^^^
My guess is that the storage access before the condition is causing the stack to be corrupted somehow.
Is this a known vulnerability? Can someone please explain what is going on?

This is a common issue when attempting to create a local reference without specifying the correct storage location.
From the Solidity docs:
There are defaults for the storage location depending on which type of variable it concerns:
state variables are always in storage
function arguments are in memory by default
local variables of struct, array or mapping type reference storage by default
local variables of value type (i.e. neither array, nor struct nor mapping) are stored in the stack
The bolded comment indicates that the line Game game; defaults to storage. If you don't initialize a storage variable, it will point to storage slot 0 by default. The end result is when you make a change to game (with game.player = msg.sender;), it will write the value out to the first slot, which will be whatever the first variable is in your contract (in this case, secretNumber).
The correct way to write this is to use Game memory game;. The compiler will give you a warning if you omit the memory keyword for this exact reason.

Related

Is possible to call a function from another contract in assembly ? Solidity

This would be the code, but my problem is, I was doing the ethernaut challenge for solidity, and my code is always running out of gas, then I think "if I use assembly it cost less gas", so I ran into a problem, it is possible to call a function getter from another contract in assembly?
This is my code:
pragma solidity ^0.6;
interface Buyer {
function price() external view returns (uint);
}
contract Shop {
uint public price = 100;
bool public isSold;
function buy() public {
Buyer _buyer = Buyer(msg.sender);
if (_buyer.price.gas(3000)() >= price && !isSold) {
isSold = true;
price = _buyer.price.gas(3000)();
}
}
}
contract ShopAttack {
Shop public challenge;
constructor(Shop _challenge) public {
challenge = Shop(_challenge);
}
function price() external view returns (uint) {
assembly {
let result
switch sload(<calling challenge.isSold() from shop>)
case 1 {
result := 99
}
default {
result := 100
}
mstore(0x0, result)
return(0x0, 32)
}
}
function attack() external {
challenge.buy();
}
pragma solidity ^0.6;
interface Buyer {
function price() external view returns (uint);
}
contract Shop {
uint public price = 100;
bool public isSold;
function buy() public {
Buyer _buyer = Buyer(msg.sender);
if (_buyer.price.gas(3000)() >= price && !isSold) {
isSold = true;
price = _buyer.price.gas(3000)();
}
}
}
contract ShopAttack {
function price() external view returns (uint) {
bool isSold = Shop(msg.sender).isSold();
assembly {
let result
switch isSold
case 1 {
result := 99
}
default {
result := 100
}
mstore(0x0, result)
return(0x0, 32)
}
}
function attack(Shop _victim) external {
Shop(_victim).buy();
}
}
I solved by calling first the method of the function to get the boolean !
with call opcode you can call the functions in contract. here is the signature of call:
call(g, a, v, in, insize, out, outsize)
g: amount of gas being sent,
a: address of the target contract,
v: amount of Ether being sent in wei,
in: starting memory location containing the data to be sent to the EVM
(which comprises the method signature and its parameter values). you would be storing parameter in slot inside assembly code
insize: size of data being sent in the hexadecimal format
out: starting memory location that should store the return data from the call
outsize: is the size of return data in hexadecimal format.
contract ShopAttack {
Shop public challenge;
constructor(Shop _challenge) public {
challenge = Shop(_challenge);
}
// you need this to call a contract function
bytes4 functionSignature = bytes4(keccak256("isSold()"));
function price() external view returns (uint returnFromAssembly) {
assembly {
let availablePointer := mload(0x40)
// function signature is 4 bytes
mstore(availablePointer,functionSignature)
// if there are more variables you could add here . each variable would take 32 bytes
// mstore(add(freePointer,0x04),firstVal)
let success := call(
100000,
challenge,
0,
availablePointer,
// since we added only function signature which has 4 bytes and pass its hex value
0x4,
availablePointer,
// This function call should store the return value at the 0x40 memory location and its length is 32 bytes
// in hex 32 is 0x20
0x20))
returnFromAssembly:=mload(availablePointer)
}
}

how to return loop and array element | Data location must be "memory" for return parameter in function | Dynamic Array

I am using the following code to return array.
function getActiveDepositIndexes() public view returns (uint256 [] storage) {
User storage user = users[msg.sender];
Deposit[] storage deposits = user.deposits;
uint[] memory indices = new uint[](deposits.length);
for(uint i = 0; i < deposits.length; i++) {
if(deposits[i].active && !deposits[i].closed){
indices.push(i);
}
}
return indices;
}
But I am getting following error,
TypeError: Data location must be "memory" for return parameter in function, but "storage" was given.
function getActiveDepositIndexes() public view returns (uint256 [] storage) {
^----------------^
Environment:
Truffle v5.1.20 (core: 5.1.20)
Solidity - 0.6.0 (solc-js)
Node v8.16.2
Web3.js v1.2.1
Seems like you explicitly specify storage for function return value. Try replacing your function signature with the following:
function getActiveDepositIndexes() public view returns (uint[]) {...}
memory is used for return values by default
Use the following code instead:
function getActiveDepositIndexes() public view returns (uint256[] memory) {
User storage user = users[msg.sender];
Deposit[] storage deposits = user.deposits;
uint[] memory indices = new uint[](deposits.length);
for(uint i = 0; i < deposits.length; i++) {
if(deposits[i].active && !deposits[i].closed){
indices[i] = i;
}
}
return indices;
}
Issues with such logic on EVM is that there is no way of returning dynamic sized memory arrays and hence push() is also not usuable with memory elements. This workaround in the above example results in zero (placeholder) alues at inactive indices according to your code...

Solidity - Truffle: Error: VM Exception while processing transaction: invalid opcode NOT because of revert

I am working on a smart contract, and I am testing it by deploying it on truffle. While it compiles fine, when I call the train() function, I get the following error:
Error: VM Exception while processing transaction: invalid opcode
After reading a bit on this, I understood it is usually caused after a revert has occurred, so I tried commenting out the 2 require functions I had just to see if it would behave differently, and it did not.
Checking out this question did not help me, or I did not see how it could.
Here is the train() function, as well as the mapping and struct type I am using in it. I should note that upon creation of a Developer, their wallet is set to 300 so I do not see how the first call of the train function by the owner could revert.
struct Developer {
address owner;
string name;
bytes32 namehash;
bytes32[] skills;
uint256[] skill_levels;
uint wallet;
}
mapping (bytes32=>Developer) public developers_all;
function train(string _name, bytes32 _skill) public {
bytes32 h = keccak256(abi.encodePacked(_name));
require(developers_all[h].owner == msg.sender, "Only the owner of the developer can train them");
require(developers_all[h].wallet >= 150, "Insufficient funds");
uint256 i = 0;
do {
if (developers_all[h].skills[i] == _skill) {
developers_all[h].skill_levels[i]++;
} else if ((i == (developers_all[h].skills.length - 1)) || (developers_all[h].skills.length == 0)) {
developers_all[h].skills.push(_skill);
developers_all[h].skill_levels.push(1);
}
i++;
} while (i < developers_all[h].skills.length);
developers_all[h].wallet = developers_all[h].wallet - 150;
}
Thank you for any help.
This is most likely because you are trying to access the first entry of an empty array. You're using a do while loop, and you're trying to access developers_all[h].skills[i] before you're checking developers_all[h].skills.length == 0, so it is possible that the array is empty at the first if statement in the do while.
You could rewrite the code to something like the following, to make sure you're never accessing an unassigned array slot.
bool foundSkill = false;
for (uint i = 0; i < developers_all[h].skills.length; i++) {
if (developers_all[h].skills[i] == _skill) {
developers_all[h].skill_levels[i]++;
foundSkill = true;
break;
}
}
if (!foundSkill) {
developers_all[h].skills.push(_skill);
developers_all[h].skill_levels.push(1);
}
Do note that looping through the whole array and doing the comparisons is quite costly, and can become impossible if the array size gets too big. You might want consider changing the structure to something like:
struct Developer {
address owner;
string name;
bytes32 namehash;
mapping(bytes32 => uint) skill_levels;
uint wallet;
}
That way you could just replace the whole thing with
developers_all[h].skill_levels[skill]++;
But you wouldn't be able to loop over the skills.

How to get keccak256 hash in Solidity

I just got started with solidity, I have used truffle to compile and deploy the code to ganache, everything works as I expect, I can call the other functions in the code, but there are certain functions that only the owner can access, the code appears to use keccak256 to get back the address calling the function and determine if the caller address is allowed, I have tried to hash my eth address using this website:
https://emn178.github.io/online-tools/keccak_256.html
and then add the hash to the code before recompiling again, but calling the owner function still throws this error:
"Error: VM Exception while processing transaction: revert"
What am i doing wrong ?
Here's the code with the original hash.
modifier onlyOwner(){
address _customerAddress = msg.sender;
require(owners[keccak256(_customerAddress)]);
_;
}
// owners list
mapping(bytes32 => bool) public owners;
function PetShop()
public
{
// add owners here
owners[0x66e62cf7a807daaf3e42f7af3befe7b2416a79ba5348820245a69fe701f80eb4] = true;
}
/*---------- Owner ONLY FUNCTIONS ----------*/
function disableDogs()
onlyOwner()
public
{
onlyDogs = false;
}
/*-----replace owner ------*/
function setOwner(bytes32 _identifier, bool _status)
onlyOwner()
public
{
owners[_identifier] = _status;
}
/*-----set price for pet adoption----*/
function setAdoptionRequirement(uint256 _amountOfTokens)
onlyOwner()
public
{
AdoptionRequirement = _amountOfTokens;
}
The keccak256 implementation in Solidity stores data differently.
keccak256(...) returns (bytes32):
compute the Ethereum-SHA-3 (Keccak-256) hash of the (tightly packed) arguments
Just use the function yourself when creating the contract:
function PetShop() public {
// add owners here
owners[keccak256(msg.sender)] = true;
}
as of now
// .encodePacked merges inputs together
owners[keccak256(abi.encodePacked(_text, _num, _addr))}=true
abi.encodePacked() , Solidity supports a non-standard packed mode where:
types shorter than 32 bytes are neither zero padded nor sign extended
dynamic types are encoded in-place and without the length.
array elements are padded, but still encoded in-place

Are there null like thing in solidity

struct buyer{
uint amount;
Status status;
}
mapping(address=>buyer) public buyers;
mapping(uint=>address) buyerIndex;
uint public buyerNum;
//Order a product.
function(){
uint doubleValue=value*2;
uint amount=msg.value/doubleValue;
if(buyers[msg.sender]==null){ //Error in this line
buyer abuyer=buyer({amount:amount,status:Status.Created}); //Error in this line
buyerNum++;
buyerIndex[buyerNum]=msg.sender;
buyers[msg.sender]=abuyer;
}else{
buyers[msg.sender].amount+=amount;
}
Order(msg.sender,amount*doubleValue,amount);
}
If a buyer is not recorded in the buyer mapping, then buyerNum++;
but I don't know how to tell whether a buyer is in the mapping
In solidity every variable is set to 0 by default.
You should think of mappings as all possible combinations are set to 0 by default.
In your specific case I would use the following:
if (buyers[msg.sender].amount == 0)
For integers:
You could create none variable to use it as a NULL:
uint256 constant NULL = 0;
Example code for check:
function isNULL(uint256 variable) internal returns (bool) {
return variable == NULL;
}
For bytes32:
You can follow different approach for bytes:
bytes32 constant NULL = "";
Example code piece:
pragma solidity ^0.6.0;
mapping(address => bytes32) public Countries;
function isCountriesInitialized(address _user) external view returns (bool)
{
if (Countries[_user] == NULL) // Returns true if `Countries[_user]` is not initialized
return false;
return true;
}
I observe that on solidity >= v0.6.0 it may return 32 for length even though it is not mapped.
Example of its returned value:
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
As Viktor said, default value for all possible values in mapping is zero. So if a buyer has not already inserted in mapping, the amount value for that address will be zero. But this approach has a flaw, if a buyer does exists but its balance became zero after some operations, you will treat it as it does not exist.
I think the best approach is to add a exists member to the buyer struct with bool type. Default value for this member is false and when the buyer get created, you initialize it with true value. So you can check exactly if a buyer exist or not via this member.
Buyer struct:
struct buyer{
uint amount;
Status status;
bool exists;
}
Initialize buyer:
buyer memory b = buyer(0, status, true);
Check if buyer exists:
if(buyers[msg.sender].exists) {
//so can buy
}
There is nothing like null in solidity.
Just check for the length of the address:
if(buyers[msg.sender].length == 0){
// do your thing
}
See also this answer on ethereum stack exchange.
Instead of using one of the values or creating an extra boolean, you can check for the byte size of the structure.
if (bytes(buyers[msg.sender]).length > 0) {
// buyer exists.
}