CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to CCS Technical Support

Preventing buffer overflow when using gets()

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
lindsay.wilson.88



Joined: 11 Sep 2024
Posts: 40

View user's profile Send private message

Preventing buffer overflow when using gets()
PostPosted: Fri Sep 27, 2024 3:01 pm     Reply with quote

Hi,

Quick Q about buffer overflows. I'm using the hardware UART and gets() to read a string in, then atoi() to convert to a number. Something like this:

Code:
char uart_text[10];
int16 mynumber;
printf("Enter a number\r\n");
gets(uart_text);
mynumber=atoi(uart_text);
// do stuff


As long as I enter 10 characters or less, the rest of the code behaves fine (briefly, it takes this as the desired frequency to have timer 2 interrupt at, works out the closest prescaler/postscaler/period values to give that frequency, and turns on timer 2). There is obviously the issue if I enter a number bigger than 65535, then mynumber goes screwy, but the rest of the code behaves correctly.

However, if I deliberately enter more than 10 characters, weird stuff happens (code works out a totally incorrect value for the timer 2 settings, and then freezes). I'm assuming this is because of a buffer overflow in uart_text messing with other memory values (sure enough, the next memory location up from uart_text is one for a variable related to calculating the frequency).

What's the easiest way to avoid stuff like this happening? Is the simplest to use a bigger buffer, e.g. uart_text[50] and just tell users to make darn sure they don't enter something ridiculous? E.g. the cat sits on the keyboard 🤣
dyeatman



Joined: 06 Sep 2003
Posts: 1934
Location: Norman, OK

View user's profile Send private message

PostPosted: Fri Sep 27, 2024 4:42 pm     Reply with quote

Making the buffer longer is one way or you could use input.c in the drivers directory that has a lot of options including length checking.
_________________
Google and Forum Search are some of your best tools!!!!
temtronic



Joined: 01 Jul 2010
Posts: 9229
Location: Greensville,Ontario

View user's profile Send private message

PostPosted: Fri Sep 27, 2024 7:49 pm     Reply with quote

also
consider using a 'timed gets() '.

if not 'timed' and the serial connection fails(either sending PC stops, wire breaks or no 'EOT' ), PIC stays in a forever loop !
CCS FAQ shows one way to do this 'timed input'.
Ttelmah



Joined: 11 Mar 2010
Posts: 19520

View user's profile Send private message

PostPosted: Sat Sep 28, 2024 12:02 am     Reply with quote

Take a deep breath. Smile

Everyone is forgetting the simple answer.
Load "input.c", and instead of using gets, use get_string.

This is a standard C function offered in this library. It is provided to deal
with exactly this problem. It behaves exactly like gets, _except_ is allows
you to specify a maximum number of characters.

On the 65535 limit, consider inputting the number into an int32 instead
of an int16, and then adding a test if the value is > 65535, and display
an error if so.

Very Happy
dyeatman



Joined: 06 Sep 2003
Posts: 1934
Location: Norman, OK

View user's profile Send private message

PostPosted: Sat Sep 28, 2024 6:57 am     Reply with quote

Yep. That's what I suggested.
Code:
 you could use input.c in the drivers directory

It has a number of options for input including get_string() and many others.
I have used it in HMIs for a quite a while now
_________________
Google and Forum Search are some of your best tools!!!!
lindsay.wilson.88



Joined: 11 Sep 2024
Posts: 40

View user's profile Send private message

PostPosted: Sun Sep 29, 2024 7:53 pm     Reply with quote

Many thanks for the tips. I've been having a play with the functions in input.c and they definitely are better! I'm having trouble understanding something though - if I send more characters over the serial port than I'm reading with the micro, why aren't they held in the buffer until next time? Meh, that doesn't make any sense. Try again:

Suppose I send 123456789 and a <CR> from computer to the PIC. If I do get_string(uart_text,5) and print the result, it receives "1234". Fair enough (although, why is it only four characters? Is the 5 including the null termination at the end?). Now, since get_string() is just using getc() repeatedly to read characters, I would have thought that once it had read the final "4", the PIC's hardware buffer would be able to store a couple more before running out of space, and I thought that these might be read by the next use of get_string(). But that doesn't seem to be the case.

Where did the characters go? Hope that makes sense.

Ehh....something's weird. If I do this:

Code:
printf("Enter text\r\n");
   while(TRUE)
   {
      c=getc();
      printf("%c\r\n",c);
   }


just to see what characters getc() receives, I'm only getting the first four characters. E.g. if I send "Hello World" from the computer, I only get "Hell" received by the PIC. Yet if I use gets() it will receive the whole thing no problem!
jeremiah



Joined: 20 Jul 2010
Posts: 1349

View user's profile Send private message

PostPosted: Sun Sep 29, 2024 10:23 pm     Reply with quote

Your printf takes time (lots of time) so by the time you are finished printing, the hardware buffer has filled up and overflowed, which means you just "miss" any characters that come after that. So you read 1 character, then print 3 (the character itself, the \r and the \n). Depending on your pic, the buffer is only so big. Most of the PIC24's I use have only a 4 byte buffer. Since you are only seeing up to 4 characters, my guess is your pic has a 4 byte hardware buffer.
Ttelmah



Joined: 11 Mar 2010
Posts: 19520

View user's profile Send private message

PostPosted: Mon Sep 30, 2024 3:16 am     Reply with quote

The get_string does not return until the <CR> is seen. It deliberately
disposes of characters beyond the specified space up to the CR.

As a general comment on storage, when you are doing something else,
you can expand the buffer available.
The hardware buffer is (normally) just under two characters.
1.9999 effectively.

When you setup the serial with #use_rs232, you can specify a software
expanded buffer.
RECEIVE_BUFFER=xx

If you set this to (say) 16, then you have a total of nearly 18 characters
of storage.

Your code must have an enable_interrupts(GLOBAL); line to make this
work. It sets up a INT_RDA handler to store the extra characters into
a buffer. Obviously costs the RAM for this and the code to add the handler
routines.
lindsay.wilson.88



Joined: 11 Sep 2024
Posts: 40

View user's profile Send private message

PostPosted: Mon Sep 30, 2024 9:38 am     Reply with quote

@jeremiah - that makes total sense now, thanks!

@ttelmah - I just had a closer look at what get_string is doing and you're right, it reads and gets rid of everything after the max characters ;-) That's a much better way to do it, and I'll be sticking with get_string() from now on. Although I don't need it writing characters back so I made a copy of the function and deleted that part.

Regarding using a larger receive buffer and interrupts, is there any risk of that screwing up the timing of other interrupts? I was aiming to only have the Timer 2 interrupt running and ensuring that there's always enough free time for the rest of the code to read in serial data. I was a little bit confused by the list of interrupts that's shown in the device header file. There's obvious ones like INT_TIMER2, INT_CCP1 etc. But something like INT_RDA - since the actual flag is called RCIF, I was expecting something similar. At least they're all shown in the help file ;-)
jeremiah



Joined: 20 Jul 2010
Posts: 1349

View user's profile Send private message

PostPosted: Mon Sep 30, 2024 3:24 pm     Reply with quote

Depending on the PIC you are using you can potentially set the timer interrupt to a higher priority than the serial interrupt, allowing it to interrupt the interrupt. Just don't share variables between them directly.
Ttelmah



Joined: 11 Mar 2010
Posts: 19520

View user's profile Send private message

PostPosted: Tue Oct 01, 2024 1:20 am     Reply with quote

OK.
As Jeriemiah says, you can use priorities, but these generally only change
the order the interrupts are serviced. On DsPIC's interrupts can interrupt
each other (if you enable nested interrupts), and on the PIC18, a high
priority interrupt can interrupt a low priority one (but only in a single
case). Normally the priority alters the order the interrupt flags are polled
when the main handler is called.

Avoiding using the serial interrupt is potentially better if timing of the
timer2 is critical.

RDA = Receive Data Available.
RCIF = Received Character Interrupt Flag.

There are a couple of really useful, but often 'missed' files it is well worth
looking at:

ints.txt
fuses.txt
readme.txt

These are all int the compiler directory, and contain useful refernences
to 'stuff'. The readme, used to be very well updated, but recently CCS
seem to only be noting very major changes in this.
lindsay.wilson.88



Joined: 11 Sep 2024
Posts: 40

View user's profile Send private message

PostPosted: Tue Oct 01, 2024 7:26 am     Reply with quote

Mmmm....sounds like I'd better just stick with the one interrupt.

Re: those text files, AWESOME! I never would have figured out to look there - I have to say, much as I like the compiler compared with the others I've tried (like the great stinking behemoth that is MPLAB 🤣) some of the documentation is a just a wee bit spotty.
Ttelmah



Joined: 11 Mar 2010
Posts: 19520

View user's profile Send private message

PostPosted: Tue Oct 01, 2024 7:33 am     Reply with quote

The manual is very good, but several other things are needed to make it all
work.:

1) This forum!... Smile
2) Those files.
3) The header for each processor.

The latter in particular is essential reading. Very Happy
jeremiah



Joined: 20 Jul 2010
Posts: 1349

View user's profile Send private message

PostPosted: Tue Oct 01, 2024 8:08 am     Reply with quote

Ttelmah wrote:
OK.
As Jeriemiah says, you can use priorities, but these generally only change
the order the interrupts are serviced. On DsPIC's interrupts can interrupt
each other (if you enable nested interrupts), and on the PIC18, a high
priority interrupt can interrupt a low priority one (but only in a single
case). Normally the priority alters the order the interrupt flags are polled
when the main handler is called.


It's not just dsPICs. On PIC24's it is the same. I've been on PIC24's for nearly 20 years now, so for me the nesting option is pretty normal (and there is no general handler in PIC24s/dsPICs). Perspective can definitely be a thing.
Ttelmah



Joined: 11 Mar 2010
Posts: 19520

View user's profile Send private message

PostPosted: Tue Oct 01, 2024 10:25 am     Reply with quote

Yes, I should have said on the 16bit PIC's rather than DsPIC's.
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group