Signal: Signal Processor Function

Posted by scoobydoo9749 on Fri, 17 May 2019 11:33:19 +0200

Signal: Signal Processor Function

1 Design signal processing function

  1. Sets a global flag variable in the signal processing function and exits. The main program periodically checks the flag. Once the flag is set, it performs the corresponding action. If the main program cannot periodically check because of the I/O state, it can write data to a dedicated pipe in the signal processing function. The main program detects the file descriptor of the read end of the pipe.
  2. After the signal processing function completes the corresponding task, terminate the process or make a non-local jump, unmount the stack, and return to the main program
1.1 reentrant function and asynchronous signal security function
  • Reentrant function: This function can be called by multiple tasks (typically threads) without interacting with each other

  • Non-reentrant function: This function cannot be called by multiple tasks, because once called, tasks interact, because some of the variables in this function may be critical resources, while one task 1 may be used, the CPU may be scheduled to another task 2, through which Task 2 changes when Task 1 does not complete its access to this critical resource.Critical resources have resulted in some errors in Task 1, so it can also be said that non-reentrant functions cannot be interrupted, to make non-reentrant functions reentrant methods:

    1. Protect critical resources with mutually exclusive or P, V operations
    2. Not suitable for global variables, static variables, malloc, free, etc.
    3. Examples of non-reentrant functions are:
    /*************************************************************************\
    *                  Copyright (C) Michael Kerrisk, 2015.                   *
    *                                                                         *
    * This program is free software. You may use, modify, and redistribute it *
    * under the terms of the GNU General Public License as published by the   *
    * Free Software Foundation, either version 3 or (at your option) any      *
    * later version. This program is distributed without any warranty.  See   *
    * the file COPYING.gpl-v3 for details.                                    *
    \*************************************************************************/
    
    /* Listing 21-1 */
    
    /* nonreentrant.c
    
       Demonstrate the nonreentrant nature of some library functions, in this
       example, crypt(3).
    */
    #if ! defined(_XOPEN_SOURCE) || _XOPEN_SOURCE < 600
    #define _XOPEN_SOURCE 600
    #endif
    #include <unistd.h>
    #include <signal.h>
    #include <string.h>
    #include "tlpi_hdr.h"
    
    static char *str2;              /* Set from argv[2] */
    static int handled = 0;         /* Counts number of calls to handler */
    
    static void
    handler(int sig)
    {
        crypt(str2, "xx");
        handled++;
    }
    
    int
    main(int argc, char *argv[])
    {
        char *cr1;
        int callNum, mismatch;
        struct sigaction sa;
    
        if (argc != 3)
            usageErr("%s str1 str2\n", argv[0]);
    
        str2 = argv[2];                      /* Make argv[2] available to handler */
        cr1 = strdup(crypt(argv[1], "xx"));  /* Copy statically allocated string
                                                to another buffer */
        if (cr1 == NULL)
            errExit("strdup");
    
        sigemptyset(&sa.sa_mask);
        sa.sa_flags = 0;
        sa.sa_handler = handler;
        if (sigaction(SIGINT, &sa, NULL) == -1)
            errExit("sigaction");
    
        /* Repeatedly call crypt() using argv[1]. If interrupted by a
           signal handler, then the static storage returned by crypt()
           will be overwritten by the results of encrypting argv[2], and
           strcmp() will detect a mismatch with the value in 'cr1'. */
    
        for (callNum = 1, mismatch = 0; ; callNum++) {
            if (strcmp(crypt(argv[1], "xx"), cr1) != 0) {
                mismatch++;
                printf("Mismatch on call %d (mismatch=%d handled=%d)\n",
                        callNum, mismatch, handled);
            }
        }
    }
    
    #include <string.h>
    /*******************
    Function function: what assignment s points to
     Return value: A pointer to the replication buffer that is dynamically opened by malloc, so when used, it is released
    ********************/
    char *strdup(const char *s);
    
    /*Function Prototype*/
    char * __strdup(const char *s)
    {
       size_t  len = strlen(s) +1;
       void *new = malloc(len);
       if (new == NULL)
          return NULL;
       return (char *)memecpy(new,s,len);
    }
    /*
    Knowable by strcpy and strdup functions
    1>strdup The function returns a pointer to the copied string, and the required space is allocated by the malloc() function and can be freed by the free() function.stdrup can directly copy the content to be copied to an uninitialized pointer because it automatically allocates space to the destination pointer.
    2>strcpy The destination pointer must be an allocated memory pointer.
    3)strdup Disadvantages:
    When using the strdup function, memory freeing is often forgotten because the action of requesting memory space is implemented within the strdup function. If you are not familiar with the implementation of this function, you will forget to use the free function to free space.
    */
    
    #define _XOPEN_SOURCE       /* See feature_test_macros(7) */
    #include <unistd.h>
    
    /***********************
    Functional function: The crypt() algorithm accepts a key up to 8 characters long and applies a variant of the data encryption algorithm (DES).The salt parameter points to a two-character string that perturbs (changes) the DES algorithm.This function returns a pointer to a 13-character string
    ***********************/
    char *crypt(const char *key, const char *salt);
    
  • Standard Asynchronous Signal Security Function: When some functions are called from a signal processor function, their implementation is guaranteed to be secure. If a function can be reentrant, or if the signal processor cannot interrupt it, it is called an asynchronous signal security function.

  • Ensure signal processor program security:

    1. Call only asynchronous signal security functions
    2. Blocking signal transmission when the main program performs unsafe functions or when it is possible to execute in a signal processor
  • Another factor contributing to insecurity is that updating errno values in the signal processor may override the errno values set when the main program calls the same function, so when the signal processor function starts to execute, the old errno values are saved, and when the signal processor function ends, the old errno values are set to the old errno values.

    void handler(int sig)
    {
     	int saveErrno;
      
      	saveErrno = errno;
      
      	/*Now we can execute a function that might modify errno*/
      
      	errno = saveErrno;
    }
    
1.2 Global variables and sig_atomic_t data types
  • In fact, signal processor functions may still need to share global variables with the main program, and sometimes read and write to global variables may have more than one machine instruction. In order to prevent processor functions from interrupting during this period, sig_atomic_t should be used to define a global variable, ensure the atomicity of read and write operations, and decorate it with volatile to prevent the compiler from optimizing it to memory.In

2 Other methods for terminating signal processor functions

  • Terminate process with_exit()
  • Kill the process with kill
  • Perform non-local jumps in signal processor
  • Use the abort() function to terminate the process and generate core storage
Perform non-local jumps in 2.1 signal processor
  • Simply put, the signal processing process can exit to the specified location when it exits, see the linux_unix system programming manual

3 SA_SAGINFO Flag

  • sigaction() creates a processor function with the SA_SIGINFO flag set. The processor function can get some additional information about the signal when it receives it, but the processor function must be declared as follows:

    void handler(int sig, siginfo *siginfo, void *ucontext)
    /*Parameters:
    siginfo: A pointer to a siginfo structure that contains additional information
    sig: Represents a signal number
    ucontent: Points to a ucontent_t type structure that mainly contains the user's context information describing the state of the process before calling the processor's process function
    */
    struct sigaction {
           void     (*sa_handler)(int);
           void     (*sa_sigaction)(int, siginfo_t *, void *);
           sigset_t   sa_mask;
           int        sa_flags;
           void     (*sa_restorer)(void);
    };
    //So be sure to initialize the sa_sigaction field when initializing
    //siginfo_t structure
    siginfo_t {
            int      si_signo;    /* Signal number */
            int      si_errno;    /* An errno value */
            int      si_code;     /* Signal code */
            int      si_trapno;   /* Trap number that caused
                                            hardware-generated signal
                                            (unused on most architectures) */
            pid_t    si_pid;      /* Sending process ID */
            uid_t    si_uid;      /* Real user ID of sending process */
            int      si_status;   /* Exit value or signal */
            clock_t  si_utime;    /* User time consumed */
            clock_t  si_stime;    /* System time consumed */
            sigval_t si_value;    /* Signal value */
            int      si_int;      /* POSIX.1b signal */
            void    *si_ptr;      /* POSIX.1b signal */
            int      si_overrun;  /* Timer overrun count; POSIX.1b timers */
            int      si_timerid;  /* Timer ID; POSIX.1b timers */
            void    *si_addr;     /* Memory location which caused fault */
            long     si_band;     /* Band event (was int in
                                            glibc 2.3.2 and earlier) */
            int      si_fd;       /* File descriptor */
            short    si_addr_lsb; /* Least significant bit of address
                                            (since kernel 2.6.32) */
    }
    

4 Interruption and restart of system calls

  • Create a signal processing function for a process

  • Initiates a blocked system call, during which a signal is passed in, interrupting the blocked system call. After the signal processing routine ends, the system call fails by default and errno is set to EINTR

  • In general, you want system calls to be restarted. Solution:

     /*Determine whether an interrupt is made by the errno value, and then decide whether to proceed with the interrupted system call*/
     while ((cnt = read(fd, buf, BUF_SIZE) == -1 && errno == EINTR))
       	continue;

     //If the code is used frequently:
     #define NO_EINTR(SMT)  while ((smt) == -1 && errno == EINTR);  
  • Specify the sigaction() of the SA_RESTART flag to create a signal handler function so that the kernel automatically restarts system calls on behalf of the process, but not all system calls will work
  1. Library functions that can be restarted
  2. Not allowed to restart:

Topics: Programming glibc