SC Lab
SC Lab
LAB RECORD
SECURE CODING
20CYS302
OCTOBER 2024
1|Page
BONAFIDE CERTIFICATE
2|Page
INDEX
Programs:
Page
Index Topic & Description
Number
1 Locale Specific Behavior 4
2 Unspecified Behavior 6
3 Implementation Defined Behavior 8
4 Undefined Behavior 9
5 Bound ,Hi, Lo, Too_far, T_size, Null Terminated, Length 11
ARRO1-C. Do not apply the size of operator to a pointer when taking the size of an
6 13
array
7 STR30-C. Do not attempt the string literals 15
STR36-C. Do not specific the bound of character array initialized with a string
8 17
literal
9 MSC34-C.Do not use deprecated or obsolescent functions 19
10 Unbounded string copy 21
11 Off-by-one errors 23
12 Null termination error 25
13 String truncation error 28
14 Caller Allocates, Caller Frees 30
15 Callee Allocates, Caller Frees 32
16 Callee Allocates, Callee Frees 34
17 MEM09-C: Do not assume memory allocation functions initialize memory 36
18 MEM11-C: Do not assume infinite heap space 38
19 MEM32-C: Detect and handle memory allocation errors 41
20 EXP34-C: Do not dereference null pointer 42
21 CWE-416: Use After Free 44
22 CWE-415: Double Free 48
23 CWE-401: Missing release of memory after effective lifetime 52
24 MEM04-C. Do not perform zero-length allocation 54
25 Integer Wraparound 56
26 Integer overflow 58
Implicit (conversions is automatically done by compiler)
27 60
and Explicit (conversions occur by programmer)
Format Specifier
1. Direct user input in format string
28 61
2. Unrestricted string copy
3. Modify variadic function
Integer addition and subtraction
29 1. Pre-condition 64
2. Post-condition
30 Race Condition 68
3|Page
Ex.No : 1
Aim:
The aim of the provided C code is to demonstrate the effect of changing the locale settings on the
output of the currency symbol in a program. It first sets the locale to Japanese (Japan) and retrieves
the currency symbol, then resets the locale to the user's default setting and retrieves the currency
symbol again.
Vulnerability:
Explanation: The program uses setlocale() to change the locale to Japanese and fetches the currency
symbol using localeconv(). It prints the Japanese currency symbol (¥) to the console.After resetting the
locale to the default, the program retrieves and prints the currency symbol again, which may vary
based on the user's environment.
Code:
#include <stdio.h>
#include <locale.h>
int main()
setlocale(LC_ALL,"ja_JP.utf8");
printf("%s\n",currentlocale->currency_symbol);
setlocale(LC_ALL,"");
currentlocale=localeconv();
printf("%s\n",currentlocale->currency_symbol);
return 0;
Output:
4|Page
Mitigation:
Explanation: The if statements check if setlocale() is successful. If not, an error message is printed, and
the program exits with a non-zero status. This enhancement improves robustness by handling
potential locale setting failures, thus preventing undefined behavior in subsequent locale-dependent
operations.
#include <stdio.h>
#include <locale.h>
int main()
{
currentlocale = localeconv();
printf("%s\n", currentlocale->currency_symbol);
return 0;
}
Output:
5|Page
EX.NO 2:
Unspecified Behavior
Aim:
The aim of the provided C code is to demonstrate the behavior of the post-increment operator (++)
when calculating the sum of two integers. The code increments the values of a and b after their
current values are used in the sum calculation.
Vulnerability:
Explanation: In the expression sum = a++ + b++, the current values of a (5) and b (7) are added
together before they are incremented. Therefore, sum is calculated as 5 + 7, resulting in 12. After the
operation, the values of a and b are incremented to 6 and 8, respectively, but this change does not
affect the computed sum.
#include <stdio.h>
int main()
printf("%d",sum);
return 0;
Output
6|Page
Mitigation:
Explanation: In the modified code, the sum is calculated directly from the original values of a and b
before any increments occur. This clarifies the sum calculation's intent. After computing the sum, the
increments are applied separately, making it clear that the result will always be the sum of the original
values, regardless of later changes to a and b. This helps prevent confusion regarding the order of
operations and the effects of the post-increment operator.
#include <stdio.h>
int main()
int a = 5, b = 7;
int sum = a + b;
a++;
b++;
printf("%d\n", sum);
return 0;
Output:
7|Page
EX.NO 3:
Aim: The aim of the provided C code is to demonstrate how to use the sizeof operator to determine
the size of an integer variable in bytes.
Vulnerability:
Explanation: The variable a is declared and initialized with the value 9. The line printf("size of int: %d",
sizeof(a)); uses the sizeof operator to get the size of the variable a, which is of type int. The sizeof
operator returns the size in bytes of the type of the operand, and in this case, it prints the size of an
integer, which is typically 4 bytes on most systems (though this may vary depending on the
architecture).
#include <stdio.h>
int main()
int a=9;
Output:
Mitigation:
Explanation: The modified code uses the format specifier %zu, which is appropriate for printing values
of type size_t, the type returned by sizeof. This ensures type safety and avoids potential warnings or
errors related to format mismatches. Adding bytes to the output string clarifies that the printed value
represents the size in bytes, making the output more informative and user-friendly.
8|Page
#include <stdio.h>
int main()
int a = 9;
printf("Size of int: %zu bytes\n", sizeof(a)); // Use %zu for size_t type
return 0;
Output:
EX.NO 4:
Undefined behavior
Aim: The aim of the provided C code is to demonstrate accessing an array element and the
consequences of trying to access an out-of-bounds index in an array.
Vulnerability:
Explanation: The array a is initialized with 5 elements, indexed from 0 to 4. The line printf("%d", a[5]);
attempts to access the element at index 5, which is out of bounds since valid indices for the array are 0
to 4. Accessing an out-of-bounds index leads to undefined behavior, which can cause the program to
crash, print garbage values, or produce unexpected results, depending on the compiler and runtime
environment.
#include <stdio.h>
int main()
int a[5]={1,2,3,4,5};
printf("%d",a[5]);
return 0;
9|Page
Output
Mitigation:
Explanation: The modified code introduces a variable index and checks if it is within the valid range (0
to 4). If the index is valid, it prints the corresponding element. If the index is out of bounds, an error
message is printed instead, preventing undefined behavior and improving the robustness of the
program by ensuring that invalid accesses are handled gracefully.
#include <stdio.h>
int main()
{
int a[5] = {1, 2, 3, 4, 5};
int index = 5; // Example index to access
} else {
printf("Error: Index %d is out of bounds!\n", index);
}
return 0;
}
Output:
10 | P a g e
Ex.No 5:
Aim: The aim of the provided C code is to demonstrate the declaration of a character array, output
various addresses of its elements, and explore the size of the array. It also attempts to access an out-
of-bounds index.
Vulnerability:
Explanation: The character array a is initialized with the string "Hello", which includes 6 characters (5
letters + 1 null terminator '\0'). The code prints:
The "Bound of arr" as 5, which is a hardcoded value.
The address of the last character (H) using &a[4].
The address of the first character using &a[0].
The size of the array using sizeof(a), which returns 6 (5 letters + 1 null terminator).
The address of &a[5], which is technically out of bounds for the valid elements of the array but
is valid for the null terminator.
#include <stdio.h>
int main()
Output:
11 | P a g e
Mitigation:
Explanation: The output for "Bound of arr" now accurately reflects the number of characters in the
string (5) by using sizeof(a) - 1. The format specifier %p is used to print addresses instead of %d, which
is more appropriate for pointer types. Casting to (void*) is recommended when printing pointers to
avoid warnings or errors. The code checks if accessing &a[5] is valid before printing its address. Since
&a[5] points to the null terminator (which is valid), it is included in the output, but the check clarifies
the intention to avoid confusion.
#include <stdio.h>
int main()
{
char a[] = "Hello"; // Array size is 6 (5 letters + null terminator)
printf("Bound of arr : %d\n", sizeof(a) - 1); // Number of letters in the string
printf("Hi of arr : %p\n", (void*)&a[4]); // Use %p for pointer (address) output
printf("Lo of arr : %p\n", (void*)&a[0]);
printf("TargetSize of arr : %ld\n", sizeof(a)); // Size of the array in bytes
return 0;
}
Output:
12 | P a g e
Ex.No 6:
ARRO1-C. Do not apply the size of operator to a pointer when taking the size of an array
Aim: The aim of the provided C code is to demonstrate how to calculate the size of an array in different
contexts, highlighting the difference between a vulnerable function that does not correctly determine
the array size and a safer function that takes the size as an argument.
Vulnerability:
Explanation: In the main function, an array a is initialized with 5 integers. The size of the array is
calculated using sizeof(a)/sizeof(a[0]), which gives the correct size (5) stored in s1. The arr_sizeVul
function attempts to calculate the size of the array using the same sizeof method, but this will not
work as intended because, in this function, the parameter arr[] decays to a pointer, and sizeof(arr)
returns the size of the pointer, not the size of the original array. The arr_size function correctly receives
the size of the array as an argument, ensuring it always has the correct size. The code prints the sizes
calculated from each method, demonstrating the issue with the vulnerable function.
#include <stdio.h>
int main(){
s1 = sizeof(a)/sizeof(a[0]);
s2 = arr_sizeVul(a);
s3 = arr_size(a,s1);
int s = sizeof(arr)/sizeof(arr[0]);
return s;
int s = size;
return s;
13 | P a g e
Output:
Mitigation:
Explanation: Function Declarations: The function declarations for arr_sizeVul and arr_size are moved
above main to ensure proper visibility and avoid implicit declaration warnings. Comments: The code
now includes comments to clarify the intention of each part, explaining why one function is vulnerable
and the other is not. Return Statements: The logic remains the same, but the comments emphasize
the importance of understanding how array parameters decay into pointers and the implications for
size calculations.
#include <stdio.h>
// Function declarations
int arr_sizeVul(int arr[]);
int arr_size(int arr[], int size);
int main() {
int s1, s2, s3, a[5] = {10, 20, 30, 40, 50};
return 0;
}
14 | P a g e
Output
Ex.No 7 :
Aim: The aim of the provided C code is to demonstrate the difference between modifying a string
stored in a character array (which is modifiable) and modifying a string literal (which is not modifiable
and leads to undefined behavior).
Vulnerability:
Explanation: The character array str2 is initialized with the string "string". The first character of str2 is
changed from 's' to 'S', and this modification is valid because str2 is an array, allowing for changes to its
contents. The modified string is printed successfully. In contrast, the pointer str1 is assigned the string
literal "string". String literals are stored in read-only memory, and attempting to modify them (like
str1[0] = 'S';) leads to undefined behavior. This can cause a segmentation fault or crash the program,
depending on the compiler and system.
#include <stdio.h>
int main(){
str2[0] = 'S';
str1[0] = 'S';
Output:
15 | P a g e
Mitigation:
Explanation: Use of const: The pointer str1 is declared as const char *str1 = "string";. This indicates
that the content pointed to by str1 should not be modified, preventing accidental modifications.
Comment on Modification: The line attempting to modify str1 is commented out to avoid undefined
behavior and clarify that this operation should not be performed. This improves code safety and
emphasizes best practices when dealing with string literals. Clear Output Messages: The print
statements differentiate between the successful modification of the character array and the safe
handling of the string literal, enhancing the code's educational value.
#include <stdio.h>
int main() {
return 0;
Output:
16 | P a g e
Ex.No 8:
STR36-C. Do not specific the bound of character array initialized with a string literal
Aim: The aim of the provided C code is to demonstrate the difference between initializing a character
array with a specified size and an unspecified size when dealing with string literals.
Vulnerability
Explanation:
String Initialization:
The character array s1 is initialized with a specified size of 3 and contains the string "abc".
However, this initialization does not leave space for the null terminator ('\0'), which means s1
does not properly terminate as a string.
The character array s2 is initialized without a specified size. This allows the compiler to
automatically determine the size, which includes the null terminator, so s2 properly contains
"abc\0".
Output:
The printf function is used to print both strings. The output for s1 may produce undefined
behavior since it lacks a null terminator. Accessing it as a string could lead to unpredictable
results when the program attempts to find the end of the string.
#include <stdio.h>
int main(){
17 | P a g e
Output:
Mitigation
Explanation: Increased Size for s1: The size of s1 is changed to 4, allowing enough space for the
three characters of the string and the null terminator. This ensures that s1 is properly null-
terminated, preventing potential undefined behavior during output.
Clear Comments: Comments are added to explain the purpose of each initialization,
improving code readability and educational value.
Safe Outputs: Both s1 and s2 are printed safely, demonstrating correct string handling in C.
#include <stdio.h>
int main() {
const char s1[4] = "abc"; // Correctly allocate space for null terminator
return 0;
Output:
18 | P a g e
Ex.No 9
Aim: The aim of the provided C code is to demonstrate the difference between using gets() and fgets()
for reading strings in C.
Vulnerability:
Explanation:
1. Using gets():
The function gets(a) is used to read a string from standard input into the character array a.
However, gets() is unsafe and deprecated because it does not perform bounds checking, which
can lead to buffer overflows if the input exceeds the size of the array. Since a is defined with a
size of 3, entering more than 2 characters will overwrite adjacent memory.
The program then prints the string read by gets(). If the input exceeds the allocated size, the
output may be unpredictable.
2. Using fgets():
The function fgets(b, 3, stdin) is used to read up to 2 characters (leaving space for the null
terminator) from standard input into the character array b. This function is safer because it
limits the number of characters read, thus preventing buffer overflows.
fgets() also retains the newline character in the input buffer if the user presses Enter before
filling the allocated space, which can affect the output.
#include <stdio.h>
int main(){
char a[3];
gets(a);
char b[3];
fgets(b,3,stdin);
19 | P a g e
Output:
Mitigation
Explanation
1. Replacing gets(): The unsafe gets() function is replaced with fgets(), which is safer and allows
specifying the buffer size.
2. Dynamic Buffer Size: The code uses sizeof(a) and sizeof(b) to avoid hardcoding buffer sizes,
making it adaptable to future changes.
3. Handling Newline Character: After reading input with fgets(), the code checks for and removes
the newline character to ensure cleaner output.
4. User Prompts: Added prompts for user input clarify expectations and improve the user
experience.
#include <stdio.h>
#include <string.h>
int main() {
char a[3];
char b[3];
20 | P a g e
}
return 0;
Output:
Ex.no 10
Vulnerability
Explanation:
String Declaration:
str is a character array of size 10, meaning it can hold up to 9 characters plus a null terminator
('\0').
str2 is a pointer to the string "This String is longer than expected........", which is much longer
than 10 characters.
Using strcpy():
The function strcpy(str, str2) copies the contents of str2 into str. However, since str2 is longer
than str, this operation causes a buffer overflow, meaning data is written past the bounds of
the str array. This can corrupt adjacent memory, lead to unpredictable program behavior,
crashes, or security vulnerabilities.
Output:
The code prints the contents of str. However, due to the buffer overflow, the output may be
incorrect or the program could crash.
21 | P a g e
#include <stdio.h>
#include <string.h>
int main(){
char str[10];
strcpy(str,str2);
printf("%s\n",str);
return 0;
Output
Mitigation
Explanation:
Using strncpy(): The strncpy(str, str2, sizeof(str) - 1) function is used to copy only the first 9 characters
of str2 into str, leaving space for the null terminator. This avoids overflow.
Manual Null Termination: str[sizeof(str) - 1] = '\0'; ensures that str is always null-terminated, which is
essential for proper string handling in C.
Safety: This approach ensures that only the allowable number of characters are copied to the
destination buffer, avoiding buffer overflows.
22 | P a g e
#include <stdio.h>
#include <string.h>
int main() {
char str[10];
return 0;
Output
Ex.no 11
Off-by-one errors
Vulnerability:
Aim: The aim of the provided C code is to demonstrate iterating over an array and printing its
elements. However, the loop in the code contains an off-by-one error, which leads to accessing
memory outside the bounds of the array.
Explanation:
Array Declaration:
Off-by-One Error:
The for loop runs from i = 0 to i <= 5. Since arrays in C are zero-indexed, the valid indices for
the numbers array are 0 through 4. However, the condition i <= 5 causes the loop to access the
numbers[5] element, which is out of bounds, leading to undefined behavior. This could result
in printing garbage values, program crashes, or other unpredictable behavior.
23 | P a g e
#include <stdio.h>
int main() {
printf("%d\n", numbers[i]);
return 0;
Output
Mitigation:
Explanation:
Loop Condition Correction: The condition in the for loop is changed to i < 5, ensuring that the loop
only iterates through valid indices (0 to 4), thus preventing the out-of-bounds access.
Safe Execution: The corrected loop prints the values of the array (1, 2, 3, 4, 5) safely without accessing
memory outside the array.
#include <stdio.h>
int main() {
printf("%d\n", numbers[i]);
return 0;
24 | P a g e
Output:
Ex.No 12
Aim: The aim of the provided C code is to concatenate two strings using strncat() and print the result.
However, the code contains a potential buffer overflow risk because the size of the destination buffer
dest is too small to hold the concatenated result.
Vulnerability
Explanation:
String Declaration:
dest is a character array of size 10, initialized with "Hello". The array has 5 characters plus the
null terminator ('\0'), leaving only 4 bytes of space for further concatenation.
Using strncat():
The strncat(dest, src, 6) function appends 6 characters from src to dest. However, dest only has
space for 4 more characters, including the null terminator. This leads to a buffer overflow, as
more characters than dest can hold are being written into it, which can result in undefined
behavior, crashes, or security vulnerabilities.
25 | P a g e
#include <stdio.h>
#include <string.h>
int main() {
return 0;
Output
Mitigation
Explanation:
Size Limitation: The strncat() function is updated to use sizeof(dest) - strlen(dest) - 1, which ensures
that we concatenate only as many characters as can fit in the remaining space in dest, leaving room for
the null terminator.
strlen(dest) calculates the current length of dest, and sizeof(dest) - strlen(dest) - 1 calculates
how much space remains in dest for the new string and the null terminator.
Safe Concatenation: This ensures no buffer overflow occurs, and the program operates safely.
26 | P a g e
#include <stdio.h>
#include <string.h>
int main() {
// Ensure we only concatenate the number of characters that fit in the remaining space
return 0;
Output
27 | P a g e
Ex.No 13
Aim: The aim of the provided C code is to safely copy a portion of a longer string (originalString) into a
smaller buffer (truncatedString) using strncpy(), ensuring that the copied string is properly null-
terminated to prevent buffer overflows.
Vulnerability:
Explanation:
String Declaration:
originalString is a long string that exceeds the size of the target buffer truncatedString, which is
only 20 bytes in size.
The goal is to safely copy part of originalString into truncatedString without causing a buffer
overflow.
Using strncpy():
Output:
The code prints the truncated version of originalString. Since the target buffer is smaller, the
string will be truncated after copying the first 19 characters.
#include <stdio.h>
#include <string.h>
int main() {
char truncatedString[20];
truncatedString[sizeof(truncatedString) - 1] = '\0';
return 0;
28 | P a g e
Output
Mitigation:
Explained:
Size Limitations: The strncpy() function is used to copy only up to sizeof(truncatedString) - 1 characters,
preventing buffer overflow.
Null Termination: After copying, the program explicitly ensures that truncatedString is null-terminated,
which is crucial to avoid undefined behavior when the string is printed.
Truncated Output: The program prints the first 19 characters of originalString, followed by the null
terminator, safely handling the string's truncation.
#include <stdio.h>
#include <string.h>
int main() {
char truncatedString[20];
// Safely copy part of the original string, leaving space for the null terminator
truncatedString[sizeof(truncatedString) - 1] = '\0';
return 0;
29 | P a g e
Output
Ex.No 14
Aim: The aim of this program is to demonstrate dynamic memory allocation in C using malloc() and
realloc(). The program allocates memory for an array, initializes and prints its values, resizes the array
using realloc(), and then deallocates the memory using free().
Explanation
#include <stdio.h>
#include <stdlib.h>
int main() {
int *arr;
int n = 5;
// Allocate memory for an array of 5 integers
arr = (int *)malloc(n * sizeof(int));
if (arr == NULL) {
printf("Memory allocation failed\n");
return 1; // Exit the program with an error code
}
// Initialize the array
for (int i = 0; i < n; i++) {
arr[i] = i + 1;
}
// Print the array elements
for (int i = 0; i < n; i++) {
printf("%d ", arr[i]);
}
printf("\n");
30 | P a g e
// Change the size of the allocated memory
n = 10;
arr = (int *)realloc(arr, n * sizeof(int));
if (arr == NULL) {
printf("Memory reallocation failed\n");
return 1; // Exit the program with an error code
}
// Initialize the new part of the array
for (int i = 5; i < n; i++) {
arr[i] = i + 1;
}
// Print the updated array elements
for (int i = 0; i < n; i++) {
printf("%d ", arr[i]);
}
printf("\n");
// Deallocate the memory
free(arr);
return 0;
}
Output
Ex.No 15
31 | P a g e
Callee Allocates, Caller Frees
Aim: The aim of this program is to demonstrate how to use a function to dynamically allocate memory
for an array, return a pointer to that memory, and properly manage the memory lifecycle using
malloc() and free().
Explanation:
Function createArray():
This function takes an integer size as a parameter and returns a pointer to an array of integers.
A check is performed to ensure that memory allocation is successful. If malloc() returns NULL,
the program prints an error message and exits.
The integer size is set to 5, meaning the array will contain 5 elements.
The function createArray(size) is called, and the pointer it returns is assigned to the variable
myArray.
After using the dynamically allocated array, the memory is freed using the free() function:
free(myArray);
32 | P a g e
#include <stdio.h>
#include <stdlib.h>
if (arr == NULL) {
arr[i] = i + 1;
return arr;
int main() {
int size = 5;
int *myArray;
myArray = createArray(size);
printf("\n");
33 | P a g e
// Free the allocated memory
free(myArray);
return 0;
Output:
Ex.No 16
Aim: The aim of this program is to demonstrate how a function can internally handle dynamic memory
allocation, usage, and deallocation without returning any pointer, thereby abstracting memory
management details away from the main program.
Explanation
Function processArray():
This function takes an integer size as a parameter to determine how many elements the array
will hold.
A check is performed to ensure that memory allocation is successful. If malloc() returns NULL,
the program prints an error message and the function exits early (return;).
Array Initialization:
In this case, processing involves printing the array elements. This is done using a loop that
prints each element of the array:
printf("%d ", arr[i]);
34 | P a g e
Memory Deallocation:
After the array is processed (printed), the memory allocated by malloc() is freed using free():
free(arr);
The integer size is set to 5, meaning the array will contain 5 elements.
The function processArray(size) is called, which handles the entire memory lifecycle
(allocation, processing, deallocation).
#include <stdio.h>
#include <stdlib.h>
if (arr == NULL) {
arr[i] = i + 1;
printf("\n");
free(arr);
35 | P a g e
}
int main() {
int size = 5;
// Call the function that handles memory allocation and deallocation internally
processArray(size);
return 0;
Output:
Ex.No 17
Aim: The aim of the provided C code is to demonstrate the difference between using malloc() (which
allocates uninitialized memory) and calloc() (which allocates and initializes memory to zero) when
dynamically allocating memory in C.
Vulnerability
Explanation:
1. Using malloc():
o The strcpy(buffer, "Hello") function is used to copy the string "Hello" into buffer. Since
"Hello" is only 6 bytes (5 characters + null terminator), it fits within the allocated
memory. However, the memory may contain uninitialized values beyond the copied
string, which can lead to undefined behavior if not handled carefully.
2. Using calloc():
o The initialized_memory() function uses calloc() to allocate 10 bytes of memory and
initialize it to zero. This ensures that all bytes in the buffer are initially set to 0.
36 | P a g e
o The same strcpy(buffer, "Hello") is used to copy the string, and the program prints the
initialized memory. This approach ensures predictable and safe behavior, as the
memory is already initialized to zero.
3. Output:
o The output will show the difference between using uninitialized and initialized
memory. While the output for both cases may appear correct, using malloc() could
lead to issues when the memory is accessed without initialization.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void uninitialized_memory() {
free(buffer);
void initialized_memory() {
strcpy(buffer, "Hello");
free(buffer);
int main() {
uninitialized_memory();
initialized_memory();
return 0;
Output
37 | P a g e
In above code both vulnerable and mitigation code is available as functions
Explanation:
Uses malloc(), but immediately calls memset(buffer, 0, 10) to initialize the allocated memory to zero
before using it. This prevents potential issues caused by uninitialized memory. Functions now include a
check to ensure that buffer is not NULL after memory allocation. This adds robustness by handling
memory allocation failures.
Ex.No 18
Aim: The aim of the provided C code is to demonstrate the behavior of the malloc() function when
attempting to allocate an extremely large amount of memory, specifically the maximum value for a
size_t type, which is generally not feasible for allocation.
Vulnerability:
Explanation
The call to malloc(size) attempts to allocate this large amount of memory, which is generally
impossible, and will likely fail.
Error Handling:
After the malloc() call, the code checks whether buffer is NULL. If the allocation fails (which it
likely will in this case), it prints a message indicating that memory allocation failed. If it
somehow succeeded (which is highly unlikely), it would print a success message and free the
allocated memory.
38 | P a g e
#include <stdio.h>
#include <stdlib.h>
void allocate_large_memory() {
if (buffer == NULL) {
} else {
free(buffer);
int main() {
allocate_large_memory();
return 0;
Output
Mitigation:
Explanation:
Size Limit Check: Before calling malloc(), the code now includes a check to see if size exceeds a
reasonable limit (in this case, 1 GB). This prevents attempts to allocate an unmanageably large amount
of memory. Output Improvement: If the size is too large, the program will inform the user that the
allocation is not attempted, avoiding unnecessary calls to malloc().
39 | P a g e
#include <stdio.h>
#include <stdlib.h>
void allocate_large_memory() {
return;
if (buffer == NULL) {
} else {
free(buffer);
int main() {
allocate_large_memory();
return 0;
Output
40 | P a g e
Ex.No 19
Aim: The aim of the provided C code is to demonstrate safe memory allocation using malloc() and
proper error handling. It allocates a small amount of memory and checks if the allocation was
successful.
Explanation:
Memory Allocation:
size is set to 100, indicating that we want to allocate enough memory for 100 bytes (which is a
reasonable amount for many use cases).
Error Handling:
The code checks if the result of malloc() (stored in buffer) is NULL. If it is NULL, this indicates
that memory allocation failed (e.g., if the system runs out of memory). In that case, the
program prints an error message and exits using exit(EXIT_FAILURE).
If the memory allocation is successful, a success message is printed, and the allocated memory
is subsequently freed with free(buffer).
#include <stdio.h>
#include <stdlib.h>
void handle_memory_allocation() {
if (buffer == NULL) {
exit(EXIT_FAILURE);
free(buffer);
41 | P a g e
int main() {
handle_memory_allocation();
return 0;
Output
Ex.No 20
Aim: The aim of this guideline (EXP34-C) is to prevent dereferencing null pointers in C code.
Dereferencing a null pointer leads to undefined behavior, which can cause program crashes, data
corruption, and security vulnerabilities. The goal is to ensure that pointers are always valid before
being dereferenced.
Vulnerability
Explanation:
A null pointer is a pointer that does not point to any valid memory location. Dereferencing such a
pointer (i.e., accessing the memory it points to) results in undefined behavior, which can manifest as
segmentation faults or other runtime errors.
#include <stdio.h>
int main() {
42 | P a g e
process_data(ptr); // This will dereference a null pointer
return 0;
Output
Mitigation
Explanation:
To mitigate the risk of dereferencing null pointers, you should always check whether a pointer is NULL
before dereferencing it. This can be achieved by adding conditional checks to ensure that the pointer
points to valid memory before any dereferencing operation.
#include <stdio.h>
#include <stdlib.h>
if (data == NULL) {
int main() {
43 | P a g e
// Example of valid pointer usage
return 0;
Output
Ex.No 21
Aim
The aim of this code is to illustrate the concept of "use after free" (UAF), which occurs when a program
continues to use a pointer after the memory it points to has been freed. This can lead to undefined
behavior, security vulnerabilities, and program crashes. The goal is to show both a vulnerable example
and a safe usage pattern to mitigate this issue.
Vulnerability
Explanation
o In the use_after_free() function, memory is allocated for a buffer, and then it is freed.
However, the code attempts to dereference the buffer pointer after it has been freed.
This is an example of UAF, which can lead to unpredictable behavior, including
program crashes.
o In the safe_memory_usage() function, after freeing the memory, the pointer is set to
NULL. This prevents accidental dereferencing of a pointer that points to freed memory,
allowing for safer memory management.
44 | P a g e
#include <stdio.h>
#include <stdlib.h>
void use_after_free() {
if (buffer == NULL) {
exit(EXIT_FAILURE);
void safe_memory_usage() {
if (buffer == NULL) {
exit(EXIT_FAILURE);
free(buffer);
if (buffer != NULL) {
} else {
45 | P a g e
}
int main() {
use_after_free();
safe_memory_usage();
return 0;
Output
Mitigation
Explanation:
1. Nullify Pointers: After freeing memory, set the pointer to NULL. This way, any subsequent
dereference checks can safely identify that the pointer no longer points to valid memory.
2. Avoid Accessing Freed Memory: Ensure that no accesses to freed memory occur after calling
free().
#include <stdio.h>
#include <stdlib.h>
void use_after_free() {
if (buffer == NULL) {
exit(EXIT_FAILURE);
46 | P a g e
// Undefined behavior: using buffer after it has been freed
void safe_memory_usage() {
if (buffer == NULL) {
exit(EXIT_FAILURE);
free(buffer);
if (buffer != NULL) {
} else {
int main() {
use_after_free();
safe_memory_usage();
return 0;
Output
47 | P a g e
Ex. No 22
Aim: The aim of this code is to demonstrate the "double free" vulnerability, where memory that has
already been freed is attempted to be freed again. This can lead to undefined behavior, which might
cause program crashes, memory corruption, or security vulnerabilities. The goal is to provide both a
vulnerable example and a safe pattern to avoid this issue.
Vulnerability
Explanation
1. Double Free:
o In the double_free() function, memory is allocated for a buffer and then freed.
However, the code attempts to free the same buffer again, resulting in a double free
vulnerability. This can corrupt the memory management data structures and lead to
unpredictable behavior.
o In the safe_memory_usage() function, after freeing the memory, the pointer is set to
NULL. This prevents the risk of double freeing the memory, as subsequent checks for
validity (i.e., checking if the pointer is NULL) will not lead to an attempt to free freed
memory.
#include <stdio.h>
#include <stdlib.h>
void double_free() {
if (buffer == NULL) {
exit(EXIT_FAILURE);
free(buffer);
48 | P a g e
void safe_memory_usage() {
if (buffer == NULL) {
exit(EXIT_FAILURE);
free(buffer);
if (buffer != NULL) {
} else {
int main() {
double_free();
safe_memory_usage();
return 0;
Output
49 | P a g e
Mitigation
Explanation
1. Nullify Pointers After Freeing: After calling free(), set the pointer to NULL. This ensures that
any subsequent free calls on the same pointer can be safely ignored since freeing a NULL
pointer is a no-op in C.
2. Avoid Double Free: Implement checks to prevent multiple free calls on the same pointer.
#include <stdio.h>
#include <stdlib.h>
void double_free() {
if (buffer == NULL) {
exit(EXIT_FAILURE);
free(buffer);
if (buffer != NULL) {
void safe_memory_usage() {
if (buffer == NULL) {
50 | P a g e
exit(EXIT_FAILURE);
free(buffer);
if (buffer != NULL) {
} else {
int main() {
double_free();
safe_memory_usage();
return 0;
Output
51 | P a g e
Ex. No 23
Aim: The aim of this code is to demonstrate the concept of a memory leak in C programming. A
memory leak occurs when a program allocates memory but fails to free it before the program
terminates, resulting in lost memory that cannot be reclaimed. The goal is to show a vulnerable
example of memory leak and a safe usage pattern to prevent it.
Vulnerability
Explanation
1. Memory Leak:
o In the memory_leak() function, memory is allocated for a buffer using malloc, but it is
never freed. This leads to a memory leak because the allocated memory remains
inaccessible and is not returned to the system.
#include <stdio.h>
#include <stdlib.h>
void memory_leak() {
if (buffer == NULL) {
exit(EXIT_FAILURE);
void safe_memory_usage() {
52 | P a g e
char *buffer = (char *)malloc(20);
if (buffer == NULL) {
exit(EXIT_FAILURE);
int main() {
memory_leak();
safe_memory_usage();
return 0;
Output
Mitigation
Explanation
1. Free Allocated Memory: Always ensure that any memory allocated with malloc, calloc, or
realloc is freed using free() before the program terminates or the pointer is reassigned.
2. Nullify Pointers (Optional): After freeing memory, it is a good practice to set the pointer to
NULL to avoid accidental dereferencing of a freed pointer.
53 | P a g e
Ex. No 24
Aim: The aim of this code is to demonstrate the behavior and implications of allocating memory with a
size of zero in C. It also highlights the difference between zero-length allocations and valid memory
allocations, providing an example of safe memory management practices.
Explanation
1. Zero-Length Allocation:
2. Safe Allocation:
#include <stdio.h>
#include <stdlib.h>
void zero_length_allocation() {
size_t size = 0;
if (buffer == NULL) {
} else {
54 | P a g e
void safe_allocation() {
if (buffer == NULL) {
exit(EXIT_FAILURE);
} else {
int main() {
zero_length_allocation();
safe_allocation();
return 0;
Output
Note: Here also both vulnerable and mitigated code is there as functions
55 | P a g e
Ex. No 25
Integer Wraparound
Aim: The aim of this code is to demonstrate the concept of integer wraparound (or overflow)
in C programming when performing arithmetic operations with unsigned integers. It also
provides a safe way to handle potential overflow situations to prevent undefined behavior in
applications.
Explanation
1. Integer Wraparound:
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
void integer_wraparound() {
unsigned int b = 1;
56 | P a g e
}
void safe_integer_addition() {
unsigned int b = 1;
if (UINT_MAX - a < b) {
} else {
int main() {
integer_wraparound();
safe_integer_addition();
return 0;
Output
Note: Here also both vulnerable and mitigated code is there as functions
57 | P a g e
Exp. No 26
Integer overflow
Aim: The aim of this code is to demonstrate the concept of integer overflow in C programming when
performing arithmetic operations with signed integers. It also provides a method to safely handle
potential overflow situations to prevent undefined behavior in applications.
Explanation
1. Integer Overflow:
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
void integer_overflow() {
int a = INT_MAX;
int b = 1;
void safe_integer_addition() {
int a = INT_MAX;
int b = 1;
58 | P a g e
if (a > INT_MAX - b) {
} else {
int result = a + b;
int main() {
integer_overflow();
safe_integer_addition();
return 0;
Output
Note: Here also both vulnerable and mitigated code is there as functions
59 | P a g e
Ex. No 27
Aim: The aim of this code is to demonstrate the concepts of implicit and explicit type conversions
(also known as type casting) in C programming. It shows how data types can be converted
automatically or manually during assignments and expressions.
Explanation
1. Implicit Conversion:
2. Explicit Conversion:
#include <stdio.h>
void implicit_conversion() {
int a = 5;
void explicit_conversion() {
double a = 5.7;
60 | P a g e
int main() {
implicit_conversion();
explicit_conversion();
return 0;
Output
Aim: The aim of this code is to demonstrate common vulnerabilities related to buffer overflows and
unsafe function usage in C programming, particularly in handling user input and string manipulation. It
illustrates potential pitfalls when using functions such as scanf, strcpy, and variadic functions with user-
controlled input.
Explanation
o The direct_user_input() function takes a string input from the user using scanf. If the
user enters a string longer than the buffer size (50 characters), this leads to a buffer
overflow, potentially overwriting adjacent memory and causing undefined behavior.
61 | P a g e
o The modify_variadic_function() uses a format specifier with a user-controlled input. If
the format string contains malicious content, it can lead to vulnerabilities like format
string attacks.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
void direct_user_input() {
char buffer[50];
void unrestricted_string_copy() {
char source[100] = "This is a long string that exceeds the buffer size.";
char destination[50];
va_list args;
va_start(args, format);
va_end(args);
62 | P a g e
void safe_variadic_function(const char* format, const char* str) {
int main() {
direct_user_input();
unrestricted_string_copy();
return 0;
Output
63 | P a g e
Ex. No 29
Aim:
The aim of integer addition and subtraction is to perform arithmetic operations on integer values while
ensuring correctness and safety, especially in the context of potential overflows or underflows.
Understanding the pre-conditions and post-conditions helps in validating the operations and ensuring
they behave as expected.
For Integer Addition and Subtraction, potential vulnerabilities arise when proper checks (pre-
conditions and post-conditions) are not enforced, leading to integer overflow or underflow issues
1. Pre-condition
2. Post-condition
This occurs when the result of an addition exceeds the maximum limit of the data type.
#include <stdio.h>
#include <limits.h>
void vulnerable_addition() {
int x = INT_MAX;
int y = 10;
int result = x + y;
int main() {
vulnerable_addition();
return 0;
64 | P a g e
Output
#include <stdio.h>
#include <limits.h>
void mitigated_addition() {
int x = INT_MAX;
int y = 10;
if (x > INT_MAX - y) {
} else {
int result = x + y;
int main() {
mitigated_addition();
return 0;
Output
65 | P a g e
2) Integer Subtraction Underflow
This happens when the result of a subtraction goes below the minimum limit of the data type.
#include <stdio.h>
#include <limits.h>
void vulnerable_subtraction() {
int x = INT_MIN;
int y = 10;
int main() {
vulnerable_subtraction();
return 0;
Output
#include <stdio.h>
#include <limits.h>
void mitigated_subtraction() {
int x = INT_MIN;
int y = 10;
if (x < INT_MIN + y) {
66 | P a g e
printf("Subtraction underflow detected! Cannot perform subtraction.\n");
} else {
int result = x - y;
int main() {
mitigated_subtraction();
return 0;
Output
67 | P a g e
Ex. No 30
Race Condition
Aim: The aim of a race condition program is to demonstrate a situation in which two or more threads
or processes access shared resources concurrently, leading to unexpected or erroneous outcomes. The
program should highlight the potential issues that arise when proper synchronization mechanisms are
not employed to ensure safe access to shared resources.
Explanation:
A Race Condition vulnerability occurs when two or more threads or processes access shared resources
concurrently, and the behavior of the program depends on the timing of their execution. This can lead
to inconsistent or unexpected results.
In this example, two threads increment a shared counter simultaneously, leading to unpredictable
results.
#include <stdio.h>
#include <pthread.h>
int counter = 0;
counter++;
return NULL;
int main() {
pthread_join(t1, NULL);
pthread_join(t2, NULL);
68 | P a g e
printf("Final counter value: %d\n", counter);
return 0;
A mutex (mutual exclusion) is used to ensure that only one thread can access the critical section
(incrementing the counter) at a time.
#include <stdio.h>
#include <pthread.h>
int counter = 0;
pthread_mutex_t lock;
pthread_mutex_lock(&lock);
counter++;
pthread_mutex_unlock(&lock);
return NULL;
int main() {
pthread_mutex_init(&lock, NULL);
69 | P a g e
pthread_join(t1, NULL);
pthread_join(t2, NULL);
pthread_mutex_destroy(&lock);
return 0;
70 | P a g e