intel/isa-l_crypto

sha1_mb/aarch64/sha1_mb_mgr_ce: rand_update_test enters an infinite loop when SHA1_MB_CE_MAX_LANES >= 3

Closed this issue · 2 comments

I found that SHA1 MultiBuffer is blocking in sha1_mb_rand_update_test when the SHA1_MB_CE_MAX_LANES is greater than 2. According to the len_done and len_rem information, when len_rem == 256 , the expected value of LAST flag is submitted, and the calculation of this flow ends. However, the test result shows that lane1's len_rem becomes a negative number and continues to execute the calculation. As a result, an infinite loop occurs.

2: len_done = 1048320
2: len_rem = 256
1: len_done = 1048320
1: len_rem = 256
0: len_done = 1048320
0: len_rem = 256
1: len_done = 1049152
1: len_rem = -576
1: len_done = 1049984
1: len_rem = -1408
1: len_done = 1050816
1: len_rem = -2240
1: len_done = 1051648
1: len_rem = -3072
1: len_done = 1052480
1: len_rem = -3904
...

After further log analysis, we find that when lanes==3, the update sequence is as follows:

-> 0 -> 1 -> 2
-> 2 -> 1 -> 0
...
-> 2  -> 1 -> 0 // last round
-> 1  -> 1 -> 1 // Error occured.
.....

Therefore, the last commit is lanes == 0 (i== 0). In this case, i++ -> i == 1, but the last request with lanes=1 has been committed. As a result, lem_rem becomes a negative number and an infinite loop occurs.

The expected order of submission should be as follows:

-> 0 -> 1 -> 2
-> 0 -> 1 -> 2
...
-> 0  -> 1 -> 2 // last round

The last submitted request should be i==2. In this case, i++ == 3 can exit the loop (when TEST_BUFS==3) or commit new lane's request (when TEST_BUFS>3).

So what's the problem?
I found the problem in the implementation of sha1_mb_mgr_init_ce in sha1_mb_mgr_ce.c:

void sha1_mb_mgr_init_ce(SHA1_MB_JOB_MGR * state)
{
	unsigned int i;

	state->unused_lanes = 0xf;
	state->num_lanes_inuse = 0;
        // look at me: Lane_idx array after initialization: [0, 1, 2]
	for (i = 0; i < SHA1_MB_CE_MAX_LANES; i++) {
		state->unused_lanes <<= 4;
		state->unused_lanes |= i;
		state->lens[i] = i;
		state->ldata[i].job_in_lane = 0;
	}

	//lanes > SHA1_MB_CE_MAX_LANES is invalid lane
	for (; i < SHA1_MAX_LANES; i++) {
		state->lens[i] = 0xf;
		state->ldata[i].job_in_lane = 0;
	}
}

Lane_idx array after initialization: [0, 1, 2], When the sha1_mb_mgr_insert_job operation is called, lane_idx is allocated from right to left. The obtained lane_idx is 2, 1, and 0. Therefore, the first returned request is the value of the first lane_idx position, which is 2. Therefore, the request of buffer0 is inserted into lane_idx=2. When sha1_mb_mgr_free_lane is called, lane_idx: 0~2 is traversed. Therefore, the result of the last commit, buffer2, is returned instead of the expected buffer0. So there's:

submit: 0        ->       1   -> 2 
return:  NULL -> NULL  -> 2
submit: 2        ->       1  -> 0
return:  1        ->       0  -> 2
...

The correct method is to initialize unsed_lanes to [2, 1, 0], just like the other modules' MGR , such as sha256_mb_mgr_init_ce or sha1_mb_mgr_init_asimd.

void sha1_mb_mgr_init_ce(SHA1_MB_JOB_MGR * state)
{
	int i;

	state->unused_lanes = 0xf;
	state->num_lanes_inuse = 0;
	for (i = SHA1_MB_CE_MAX_LANES - 1; i >= 0; i--) {
		state->unused_lanes <<= 4;
		state->unused_lanes |= i;
		state->lens[i] = i;
		state->ldata[i].job_in_lane = 0;
	}

	//lanes > SHA1_MB_CE_MAX_LANES is invalid lane
	for (i = SHA1_MB_CE_MAX_LANES; i < SHA1_MAX_LANES; i++) {
		state->lens[i] = 0xf;
		state->ldata[i].job_in_lane = 0;
	}
}

Why is there no error in the original SHA1_MB_CE_MAX_LANES==2 ? After the test, we find that the same problem occurs, but the infinite loop is not triggered:

...
0 update
0, 1047488, 1088

1 update
1, 1048320, 256    # buffer1 update with LAST

0 update
0, 1048320, 256    # buffer0 update with LAST

i = 0 completed. 
1, 1048320, 256    # buffer1 is submitted twice

i = 1 completed.   # It's just that because buffer1 is also completed, there's no infinite loop.
...

This is now fixed, thanks!