Function sscanf must be assigned to variable otherwise strange behavior - c

Consider this code:
#define TRANSLATOR_requestElectricityMeterWrite() do{addr = word_getAddress(); value = word_getValue(); }while(0)
uint16_t value;
uint8_t addr;
bool dispatcher(void)
{
TRANSLATOR_requestElectricityMeterWrite();
return true;
} // AFTER this point (during debug) program goes to default handler
int main(void)
{
if(dispatcher())
continue;
. . . .
. . . .
}
uint16_t word_getValue(void)
{
uint16_t value;
sscanf("ABCD", "%4x", (unsigned int *)&value);
return value;
}
uint8_t word_getAddress(void)
{
uint8_t address;
sscanf("00", "%2x", (unsigned int *)&address);
;
return address;
}
When the code above is run, the statement inside if causes program to crash(goes to some default handler).
But when I change the two(word_getValue and word_getAddres) functions to this:
uint16_t word_getValue(void)
{
uint16_t value;
int i = 0;i++;
i = sscanf(WORD_getValueString(), "%4x", (unsigned int *)(&value));
return value;
}
uint8_t word_getAddress(void)
{
uint8_t address;
int i = 0;i++;
i = sscanf(WORD_getNameString(), "%2x", (unsigned int *)(&address));
return address;
}
It works. The addition if the dummy i seems to solve that problem. But why doesn't it work the other way?
GNU ARM v4.8.3 toolchain

Both functions invoke undefined behavior, hence anything can happen. Adding an extra local variable changes the location of the destination variable, hiding the effect of its incorrect size.
sscanf("ABCD", "%4x", (unsigned int *)&value);
sscanf will store sizeof(unsigned int) bytes (probably 4) into variable value, which has only 2 bytes.
sscanf(WORD_getNameString(), "%2x", (unsigned int *)(&address));
Will store sizeof(unsigned int) bytes into variable address, which has only 1 byte.
The easiest way to fix this problem is to parse into an unsigned int and store the parsed value to the destination separately, or simply return the value:
uint16_t word_getValue(void) {
unsigned int value;
if (sscanf(WORD_getValueString(), "%4x", &value) == 1)
return value;
// could not parse a value, return some default value or error code
return 0;
}
uint8_t word_getAddress(void) {
unsigned int address;
if (sscanf(WORD_getNameString(), "%2x", &address) == 1)
return address;
// could not parse a value, return some default value or error code
return 0;
}
You might also want to verify if the parsed value is within range for the destination type, but since you limit the parse to respectively 4 and 2 hex digits, overflow cannot happen.

%x format requires unsigned argument (suppose it's uint32_t on your platform). If you pass uint16_t or uint8_t it can corrupt memory. In your case it corrupt stack and overwrites return address. Try use %4hx for uint16_t and %2hhx for uint8_t.

Related

ioctl giving error in different executions

For the next system: VAR-DART-MX8M (https://variwiki.com/index.php?title=DART-MX8M)(It is a ARM A-53 cortex based system)
Kernel version: Linux Debian Stretch version (kernel 4.14.78)
I use Eclipse to write the code and then cross compile to the specified system.
I want to use an SPI bus in my embedded system to communicate with an FPGA. I have read that casting pointers can lead to undefined behaviour: Passing pointer of unsigned int to pointer of long int
The problem is that I have to cast some parameters in order to meet the spi_ioc_transfer struct requirements:
int transfer16(int fd, uint16_t *tx, uint16_t *rx, uint32_t len){
int ret;
errno=0;
tr.tx_buf = (unsigned long)tx;
tr.rx_buf = (unsigned long)rx;
tr.len = len;
tr.delay_usecs = 1;
tr.speed_hz = spi_speed;
tr.bits_per_word = 16;
ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
if (errno != 0){
printf("SPI IOCTL ret(%d), error(%d) %s\n", ret, errno,
strerror(errno));
}
return ret;
}
Where tr is the struct spi_ioc_transfer provided by spidev.h (declared as global variable).
The function transfer16 is called by the following function:
int send_command_readadc(int fd, int16_t *rx, uint16_t ndata_adc, uint8_t membank) {
int ret;
uint16_t tx[2];
uint16_t crc16_o, crc16_i;
uint8_t rcommand;
uint8_t ack;
uint32_t len = 2*(ndata_adc+NUMELS(tx)+2); // 2*sizeof(rx)
int i = 0;
tx[0] = (THE_READADC_COMMAND << 8) + membank;
tx[1] = crc16_uint16_false(tx,NUMELS(tx)-1);
ret = transfer16(fd, tx, rx, len);
if (ret==-1) {
return -1;
}
And I call this function using the following parameters:
ret = send_command_readadc(fd_spi, (int16_t *)(data_adc+(ndata_adc+4)*i), ndata_adc, membank);
Where data_adc is a pointer to a allocated part of memory where I want to save the ADC readings:
data_adc = (int16_t *) calloc((ndata_adc+4)*M,sizeof(int16_t));
So, for example, if M is 3 I save the quantity of (ndata_adc+4) starting from the register data_adc. Then (ndata_adc+4) starting from the register data_adc+(ndata_adc+4) etc.
The function transfer16 sometimes doesn't work properly, as same exact executions can have different results:
Any tip on how to proceed with this issue?
If you need more information about the problem, ask without hesitation.
Thanks!
I have solutioned this problem this way:
Instead of using data_adc = (int16_t *) calloc((ndata_adc+4)*M,sizeof(int16_t)); , I declared an array int16_t data_adc_static[(ndata_adc+4)*M]; and I give the next address int16_t *address = &data_adc_static[(ndata_adc+4)*i]; to the send_command_readadc command.
This way the problem that I had desappeared.
Thanks everybody for your answers

MPU6050 motion driver library function call cause hardfault exception

Hi I'm try to use mpu6050 on my stm32 project.
I copy the motion driver library from SparkFun_MPU-9250-DMP_Arduino_Library and replace arduino function like arduino_i2c_write to stm32 write function. The replace part work fine. The stm32 board did write bytes into the imu and read from it.
However, when I try to setup the dmp funtion and use the mpu_load_firmware function, I encouter a weird situation.
int mpu_load_firmware(unsigned short length, const unsigned char *firmware,
unsigned short start_addr, unsigned short sample_rate)
{
unsigned short ii;
unsigned short this_write;
/* Must divide evenly into st.hw->bank_size to avoid bank crossings. */
#define LOAD_CHUNK (16)
unsigned char cur[LOAD_CHUNK], tmp[2];
if (st.chip_cfg.dmp_loaded)
/* DMP should only be loaded once. */
return -1;
if (!firmware)
return -1;
for (ii = 0; ii < length; ii += this_write) {
this_write = min(LOAD_CHUNK, length - ii);
if (mpu_write_mem(ii, this_write, (unsigned char*)&(firmware[ii])))
return -1;
if (mpu_read_mem(ii, this_write, cur))
return -1;
if (memcmp(firmware+ii, cur, this_write))
return -2;
}
/* Set program start address. */
tmp[0] = start_addr >> 8;
tmp[1] = start_addr & 0xFF;
if (i2c_write(st.hw->addr, st.reg->prgm_start_h, 2, tmp))
return -1;
st.chip_cfg.dmp_loaded = 1;
st.chip_cfg.dmp_sample_rate = sample_rate;
return 0;
}
When the code run to the line if (mpu_write_mem(ii, this_write, (unsigned char*)&(firmware[ii]))), it cause a hardfault. And I found out that the code cannnot call the mpu_write_mem function porperly. When I start to step in the function, the hardfault exception immediately occurred. I cannot figure it out what cause the exception and how to fix it.
I've checked the pointer, &(firmware[ii]), address, it looks just fine. But the weired thing is that in the mpu_write_mem function, the argument value all differ from the mpu_load_firmware function.(mem_addr != ii, length != this_write, ...) I'm not sure if this cause the exception or the other way around.
Can anyone give me some idea? Thank you very much ~~
Here is the mpu_write_mem function, both function are in the inv_mpu.c file
int mpu_write_mem(unsigned short mem_addr, unsigned short length,
unsigned char *data)
{
unsigned char tmp[2];
if (!data)
return -1;
if (!st.chip_cfg.sensors)
return -1;
tmp[0] = (unsigned char)(mem_addr >> 8);
tmp[1] = (unsigned char)(mem_addr & 0xFF);
/* Check bank boundaries. */
if (tmp[1] + length > st.hw->bank_size)
return -1;
if (i2c_write(st.hw->addr, st.reg->bank_sel, 2, tmp))
return -1;
if (i2c_write(st.hw->addr, st.reg->mem_r_w, length, data))
return -1;
return 0;
}

Function parameters as pointers and return value in C

In the function i need to pass in myFlg and somVar as input parameters. They are modified withing the function and then returned newLevel.
Do i pass in pointers for myFlg and somVar and how do i do that. The flg and counter need to be updated inside the function but they are not globals. So pass in two parameters/pointers and return a value, one return value and two modified values/pointers.
in flags.h
#define FlagReg1set_u 0b01000001
typedef union
{
struct
{
uint8_t Flg1 : 1;
uint8_t Flg2 : 1;
uint8_t Flg3 : 1;
uint8_t Flg4 : 1;
uint8_t Flg5 : 1;
uint8_t Flg6 : 1;
uint8_t my_Flg : 1;
uint8_t foo : 1;
} bits;
uint8_t byte;
} FlagReg1_t = FlagReg1set_u;
in level.c
uint16_t level(uint8_t Flag, uint8_t SomeVariable, uint8_t SomeCount)
{
static uint16_t newLevel = SomeVariable;
static uint8_t count = SomeCount;
static uint8_t somVar;
if (FlagReg1.bits.my_Flg == 1)
{
/* do something */
newLevel = 0xFFFF;
}
else
{
FlagReg1.bits.my_Flg = 0
}
if (somVar == SomeVariable)
{
count = 0;
}
else
{
count++;
}
return newLevel
}
in main .c
#include "flags.h"
main()
{
/* Variable Declaration */
uint16_t level(uint8_t Flag, uint8_t SomeVariable, uint8_t SomeCount);
uint8_t count = 0;
uint8_t ownlevel;
uint16_t newLevel;
level(FlagReg1_t my_Flg, ownlevel, count);
if (newLevel == 0)
{
//do something
}
else
{
//do something
}
}
Well excuses to the community I use answer space , this is more a comment but I need the tools to explain.
So , eumac , the usual purpose of a union is:
typedef union eumac_un_t
{
struct eumac_str
{
uint8_t flag_4;
uint8_t flag_3;
uint8_t flag_2;
uint8_t flag_1;
};
int the_flag;
}eumac_un;
This takes 4 bytes (a memory area) , stores the same data which can be retrieved in all the various ways.
Example if you store
eumac_un_t.eumac_str.flag_1 = 150 //(1001 0110)
eumac_un_t.eumac_str.flag_2 = 20 //(0001 0100)
what you get for
eumac_un_t.the_flag == 5270 //its 0000 0000 - 0000 0000 - 0001 0100 - 1001 0110
This is why we use a union.So in your case you won't need it.Is that clear??
Now in your case , usually you pass as pointers when you have an array.If flag is just an integer , then you can simply pass it as an integer.
So yes , exactly the function you mention is very nice
uint16_t level(FlagReg1_t *ptrToFlagReg1, uint8_t *SomeVariable, uint8_t *SomeCount)
{
ptrToFlagReg1->something = something;
ptrToFlagReg1->something_else = something_else;
return number_of_something;
}
Integers don't really have to be passed as pointers if there's no need to.
uint8_t SomeVariable, uint8_t SomeCount

Data loss when converting byte to int on Arduino

I have a 3-byte array that buffers incoming bytes through the Serial port. Once it is full I want to use the bytes to call a function that takes a byte and an int as a parameter. This should theoretically not be a problem, but for some reason the bytes are not being converten into an int properly. Here is the code I have:
// for serialEvent()
uint8_t buffer[3] = {0, 0, 0};
uint8_t index = 0;
void serialEvent() {
while (Serial.available()) {
if (index > 2) {
// buffer is full so process it
uint16_t argument = (uint16_t)buffer[1];
argument <<= 8;
argument |= buffer[2];
processSerial(buffer[0], argument);
index = 0;
}
buffer[index] = Serial.read();
index++;
}
}
void processSerial(uint8_t action, uint16_t argument) { ... }
The problem appears to be in the line where the first bit is shifted to the left to make space for the second one. I have tried outputting the variable over the Serial port again and after the bit shift operation, it is 0.
The same thing happens when I try to replace the bit shift operation with a multiplication by 256 (which has the same result in theory).
Irritatingly, when I assign a static value like so, everything works fine:
uint16_t argument = 0x00CD;
argument <<= 8;
Is this a type cast problem? Am I missing something here?
Have not found the solution as to why this happens, but using the word() function does exactly what I want:
uint16_t argument = word(buffer[1], buffer[2]);
processSerial(buffer[0], argument);
Annoyingly, it is defined as:
unsigned int makeWord(unsigned char h, unsigned char l) { return (h << 8) | l; }

How to change the values of socketid is changed after calling function

I am using following api to initialize sockfd at client side:(sockfd=3)
if ((sockfd = socket(p->ai_family, p->ai_socktype,p->ai_protocol)) == -1) {
perror("client: socket");
continue;
}
& initializing my TPKT_Buff to {3,0,0,0} value by using function :
if(Fill_TPKT(PStack,TPKT_Buff) != 0)
{
printf("Error while filling TPKT Buffer");
return 1;
}printf("tpkt/2_Buff%x %x\n",TPKT_Buff[0],TPKT_Buff[1]);printf("sockfd=%d\n",sockfd);
But, after calling function :
if(Fill_COTP(PStack,&cotp) != 0)
{
printf("Error while filling COTP Structure!");
return 1;
}
my socfd & TPKT_Buff values changed to zero TPKT_Buff={0,0,0,0} & sockfd=0 :
printf("sockfd=%d\n",sockfd);
printf("TPKT/2_Buff=%x %x\n",TPKT_Buff[0],TPKT_Buff[1]);
Definitions of functions Fill_COTP & Fill_TPKT are as follows :
int Fill_TPKT(FILE *fptr,unsigned char *buf)
{
fseek(fptr,14,SEEK_SET);
fscanf(fptr,"%d",buf+0);
fseek(fptr,15,SEEK_CUR);
fscanf(fptr,"%d",buf+1);
return 0;
}
int Fill_COTP(FILE *fptr, COTP *cotp)
{
unsigned short temp;
fseek(fptr,13,SEEK_CUR);
fscanf(fptr,"%d",&temp);
cotp->Destination_Ref[1] = temp;
cotp->Destination_Ref[0] = temp>>8;
printf("%x %x\n",cotp->Destination_Ref[0],cotp->Destination_Ref[1]);
fseek(fptr,13,SEEK_CUR);
fscanf(fptr,"%d",&temp);
cotp->Source_Ref[1] = temp;
cotp->Source_Ref[0] = temp>>8;
printf("%x %x\n",cotp->Source_Ref[0],cotp->Source_Ref[1]);
fseek(fptr,14,SEEK_CUR);
fscanf(fptr,"%d",&temp);
cotp->Source_Tsap[1] = temp;
cotp->Source_Tsap[0] = temp>>8;
printf("%x %x\n",cotp->Source_Tsap[0],cotp->Source_Tsap[1]);
fseek(fptr,14,SEEK_CUR);
fscanf(fptr,"%d",&temp);
cotp->Destination_Tsap[1] = temp;
cotp->Destination_Tsap[0] = temp>>8;
printf("%x %x\n",cotp->Destination_Tsap[0],cotp->Destination_Tsap[1]);
fseek(fptr,17,SEEK_CUR);
fscanf(fptr,"%d",&(cotp->TPDU_size));
printf("%x\n",cotp->TPDU_size);
return 0;
}
Here PStack is a file pointer.
I am not getting why my sockfd & TPKT_Buff values changing to zero even I am not using these values in my function Fill_COTP();
Please give some suggestion.
Definition of COTP is:
typedef struct
{
unsigned char PDU_type;
unsigned char Destination_Ref[2];
unsigned char Source_Ref[2];
unsigned char Source_Tsap[2];
unsigned char Destination_Tsap[2];
unsigned char TPDU_size;
} COTP;
There is no relation between sockfd & TPKT_Buff.
The trouble appears to be in the line:
fscanf(fptr,"%d",&(cotp->TPDU_size));
Your TPCU_size is unsigned char TPDU_size; which is only 1 byte (assuming this to be the size of 'char') in size, but you are trying put 4 bytes (assuming that to be the size of 'int') into it during fscanf, thereby potentially overwriting the memory around it.
While there is some information missing, some of what you have shown is clearly wrong and is likely to be involved in the problem. For instance:
int Fill_TPKT(FILE *fptr,unsigned char *buf)
{
fseek(fptr,14,SEEK_SET);
fscanf(fptr,"%d",buf+0);
fseek(fptr,15,SEEK_CUR);
fscanf(fptr,"%d",buf+1);
If each call to fscanf works, each will fill in one int, but buf points to a sequence of unsigned chars. Unless you have very large chars and sizeof(int) == 1 this is obviously wrong.
The same mistake is repeated at many other points, e.g., in Fill_COTP, fscanf with a %d directive is used to fill in temp, which has type unsigned short rather than int.
You could change the directives (%hhd will fill in a single char and %hd will fill in a single short; %hhu and %hu will fill in unsigned char and unsigned short). However, simply calling fscanf like this, without any error checking, is not very robust. If the contents of the input stream are not convert-able to the target type, the call will fail (fscanf will return either EOF or a short count, depending on the kind of failure, "input" vs "matching", and the point of the failure). You might want a little intermediate function that does appropriate error checking, perhaps scanning into an int after all and then range-checking the value for instance.

Resources