scanf C Function – Syntax, Examples, and Security Best Practices

Igal Zeifman
Igal Zeifman

5  min read | min read | 09/08/2023

What Is the scanf() C Function

In C programming, the scanf() function is used to read formatted input from the standard input device (stdin). The< code>scanf() function is part of the standard library stdio.h and is used to receive data from the user. The syntax of the function is as follows:

int scanf( const char *format, ... );

This function reads the data, formats it according to the parameter format, and then stores the data in the addresses of the variables passed. In essence, the scanf() function allows us to interact with our programs and input data directly.

Understanding the scanf() function is important for any C programmer. It allows you to create interactive programs, capture user input, and manipulate data effectively. At the same time, the improper use of scanf can open the door to buffer overflow vulnerabilities.

Common Use Cases for the scanf() Function

 

Reading Single and Multiple Values

One of the most common uses of the scanf() C Function is reading single and multiple values from user input. This function can read a variety of data types, including integers, floating-point numbers, and characters. For instance, if you want to read an integer, you would use the %d format specifier, and for a floating-point number, you would use %f.

Reading multiple values is also straightforward. You simply need to include multiple format specifiers and variables in the function. For example, if you want to read two integers, you would use:

scanf("%d %d", &num1, &num2);

This tells the function to read two integers and store them in the variables num1 and num2.

Reading Strings and Lines of Text

The scanf() function is also capable of reading strings and lines of text. This is done using the %s format specifier. For instance, to read a string, you would use:

scanf("%s", str);

This tells the function to read a string and store it in the variable str. However, the scanf() function stops reading a string when it encounters a white space. To read a full line of text, including white spaces, you can use the fgets() function instead. This function reads a line of text until it encounters a newline character or reaches the end of the specified length.

Reading Complex Data Structures

The scanf() function can also read complex data structures, such as arrays and structures. For instance, to read an array of integers, you would use a loop with the scanf() function inside it.

Each iteration of the loop would read a single integer and store it in the current index of the array. Similarly, to read a structure, you would use the scanf() function for each member of the structure.

Syntax and Code Example

Let’s take a deeper look at how the scanf() C function works. The syntax of the function is as follows:

scanf(const char *format, ...);

The function takes two parameters: a constant character pointer to a format string, and a variable number of arguments. The format string specifies the type and quantity of data to be read, and the arguments are the variables in which to store the data.

Here’s a simple code example that demonstrates how it works:

#include <stdio.h>

int main() 
{
    int num;
    printf("Enter a number: ");
    scanf("%d", &num);
    printf("You entered: %d", num);
    return 0;
}

In this example, the program prompts the user to enter a number. The scanf() function then reads the number and stores it in the variable num. The program then prints the number back to the user.

scanf() Alternatives

gets() and fgets() Functions

The syntax of these functions is as follows:

char *gets(char *str)
char *fgets (char *str, int n, FILE *stream);

The gets() and fgets() functions are common alternatives to scanf():

  • The gets() function reads a line from stdin into the buffer pointed to by str, until it reaches either a terminating newline or EOF.
  • In contrast, the fgets() function reads in at most (n-1) characters, where n is the length of the string, and stores them into the buffer pointed to by the str ariable. Both functions have their unique advantages and drawbacks.

The gets() function does not respect the buffer’s boundaries, which can lead to a buffer overflow. On the other hand, fgets() pays respect to the buffer’s boundaries, thus it is more secure than gets().

However, note that fgets() leaves the newline character in the input buffer, which needs to be handled properly to avoid unexpected behavior.

fgetc() and fscanf() Functions

The syntax of these functions is as follows:

int fgetc(FILE *pointer)
int fscanf(FILE *stream, const char *format, ...)

The fgetc and fscanf functions are two other alternatives to scanf:

  • fgetc() reads the next character from the stream and returns it as an unsigned char cast to an int, or EOF on end of file or error. It is excellent for reading formatted input from files
  • fscanf() reads data from the stream and stores it according to the parameter format into the locations pointed by the additional arguments. It can be used to read a file character by character

In terms of security, the fscanf() function, can lead to buffer overflows if not properly implemented. The fgetc() function is not as vulnerable, but it can present other risks:

  1. It reads input character by character, which can be slower and might expose to attacks where the attacker controls the input rate (DoS)
  2. Without proper error and EOF handling, it can potentially lead to unexpected program behavior

Security Best Practices

Here are a few best practices that can help you use the scanf() function safely and avoid security vulnerabilities.

Check Return Values

When using the scanf(), always remember to check its return value, which indicates the number of items successfully read. If the return value is less than the number of items you tried to read, it means that an error has occurred or the end of file has been reached.

Limit String Length

When using scanf() to read strings, it’s important to limit the string length to prevent buffer overflow. This can be done by using the width specifier in the format string. For example, if you have a character array of size 100, you can use this code to read a string:

scanf("%99s", str)

This will ensure that the function reads at most 99 characters, leaving one space for the null terminator.

Be Careful with %n

The %n format specifier in scanf() is used to store the number of characters read so far. However, it’s important to be aware that %n does not consume any input and can lead to unexpected results if not used correctly. Thus, it’s advisable to use %n with caution.

Always Initialize Variables

Before using a variable with scanf(), always be sure to initialize it. Using uninitialized variables can lead to undefined behavior and hard-to-find bugs. Thus, it’s a good practice to always initialize your variables – here an in general.

Deterministic Security for IoT

The vast majority of IoT/embedded devices use C code, and are prone to related memory and code vulnerabilities, including vulnerabilities stemming from improper use of scanf and similar functions.

Sternum’s patented EIV™ (embedded integrity verification) technology protects from these with runtime (RASP-like) protection that deterministically prevents all memory and code manipulation attempts, offering blanket protection from a broad range of software weaknesses (CWEs).

Embedding itself directly in the firmware code, EIV™ is agentless and connection agnostic. Operating at the bytecode level, it is also universally compatible with any IoT device or operating system (RTOS, Linux, OpenWrt, Zephyr, Micirum, FreeRTOS, etc.) and has a low overhead of only 1-3%, even on legacy devices.

The runtime protection features are also augmented by (XDR-like) threat detection capabilities of Sternum’s Cloud platform, its AI-powered anomaly detection, and extended monitoring capabilities.

To learn more, check out these case studies of how this technology was used to:

Help a Fortune 500 company catch memory leaks in pre-production (Zephyr device)

Uncover buffer overflow vulnerabilities in 80,000 NAS devices

Uncover buffer overflow vulnerability in a very popular smart plug device

Also check out the video below to see Sternum EIV™ in action, as it provides out-of-the-box mitigation of Ripple20 malware, used for memory corruption attacks.

JUMP TO SECTION

Enter data to download case study

By submitting this form, you agree to our Privacy Policy.