Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Wrap pointers to s2n-bignum functions - delocator fix #2165

Merged
merged 2 commits into from
Feb 6, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 23 additions & 3 deletions crypto/fipsmodule/ec/p384.c
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,26 @@ static p384_limb_t p384_felem_nz(const p384_limb_t in1[P384_NLIMBS]) {

#endif // EC_NISTP_USE_S2N_BIGNUM

// The wrapper functions are needed for FIPS static build.
// Otherwise, initializing ec_nistp_meth with pointers to s2n-bignum
// functions directly generates :got: references that are also thought
// to be local_target by the delocator.
Comment on lines +86 to +89
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the difference between defining this wrapper function and how s2n-bignum defines bignum_add_p384? Is my mental model of C functions are equivalent to functions defined in the assembly files wrong?

Copy link
Contributor Author

@nebeid nebeid Feb 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a valid question. This change stemmed from the observation that those 3 functions that were wrapped, when they were being assigned to the p521_methods struct in crypto/fipsmodule/ec/p521.c#L292-L296, they were creating the following (commented) assembly instructions that were replaced by delocate.go by the ones right underneath in <build-folder>/crypto/fipsmodule/bcm-delocated.S.

.Lp521_methods_init_local_target:
p521_methods_init:
...
// WAS adrp x10, :got:bignum_add_p521
adr x10, .Lbignum_add_p521_local_target
// WAS adrp x9, :got:bignum_sub_p521
adr x9, .Lbignum_sub_p521_local_target
// WAS adrp x6, :got:bignum_neg_p521
adr x6, .Lbignum_neg_p521_local_target

In the case of gcc compilations (not all versions, it depends which ones get lucky), the target address of the adr instructions was further than the admissible 1MB.
The observation that other s2n-bignum functions assigned to the same method such as bignum_sqr_p521_alt was accessed via bignum_sqr_p521_selector which is defined in the header file

static inline void bignum_mul_p521_selector(uint64_t z[S2N_BIGNUM_STATIC 9], const uint64_t x[S2N_BIGNUM_STATIC 9], const uint64_t y[S2N_BIGNUM_STATIC 9]) {
and wasn't causing the same problem, rather in bcm-delocated.S:

// WAS adrp	x7, bignum_sqr_p521_selector
	adr x7, .Lbignum_sqr_p521_selector_local_target

and not too far from it

.align 2
.p2align 4,,11
.type bignum_sqr_p521_selector, %function
.Lbignum_sqr_p521_selector_local_target:
bignum_sqr_p521_selector:
.LFB423:

.cfi_startproc
// WAS adrp x2, :got:OPENSSL_armcap_P
    sub sp, sp, 128
    stp x0, x30, [sp, #-16]!
    bl .LOPENSSL_armcap_P_addr
    mov x2, x0
    ldp x0, x30, [sp], #16
    add sp, sp, 128
// WAS ldr x2, [x2, #:got_lo12:OPENSSL_armcap_P]
    ldr w2, [x2]
    tst w2, 28672
    beq .L453
// WAS b bignum_sqr_p521_alt
    b .Lbignum_sqr_p521_alt_local_target

the b (branch) instructions on the last line doesn’t suffer from the same range limit as the adr instruction; b has a range of of +/- 32MB, while adr has a range of +/- 1MB.

That's why I thought that wrapping the s2n-bignum function which may be far with a "nearby" static function may result in the same pattern as with bignum_sqr_p521_selector

Before this PR, i.e. adding the wrappers, the initialiser of p521_methods was compiled to get the address of, e.g., bignum_add_p521 from an unknown location and assign it to a register, but delocator.go found that label within the module and considered it local, but was unluckily too far to be treated like other local labels.
Adding a wrapper function caused the address of a "closer" function to be put in the address and a branch instruction to that target, which the delocator treated still as local but didn't need to change the b instruction because it doesn't cause relocation like adrp does.

static inline void p384_felem_add_wrapper(ec_nistp_felem_limb *c,
const ec_nistp_felem_limb *a,
const ec_nistp_felem_limb *b) {
p384_felem_add(c, a, b);
}

static inline void p384_felem_sub_wrapper(ec_nistp_felem_limb *c,
const ec_nistp_felem_limb *a,
const ec_nistp_felem_limb *b) {
p384_felem_sub(c, a, b);
}

static inline void p384_felem_neg_wrapper(ec_nistp_felem_limb *c,
const ec_nistp_felem_limb *a) {
p384_felem_opp(c, a);
}

static void p384_from_generic(p384_felem out, const EC_FELEM *in) {
#ifdef OPENSSL_BIG_ENDIAN
Expand Down Expand Up @@ -273,11 +293,11 @@ static void p384_point_add(p384_felem x3, p384_felem y3, p384_felem z3,
DEFINE_METHOD_FUNCTION(ec_nistp_meth, p384_methods) {
out->felem_num_limbs = P384_NLIMBS;
out->felem_num_bits = 384;
out->felem_add = bignum_add_p384;
out->felem_sub = bignum_sub_p384;
out->felem_add = p384_felem_add_wrapper;
out->felem_sub = p384_felem_sub_wrapper;
out->felem_mul = bignum_montmul_p384_selector;
out->felem_sqr = bignum_montsqr_p384_selector;
out->felem_neg = bignum_neg_p384;
out->felem_neg = p384_felem_neg_wrapper;
out->felem_nz = p384_felem_nz;
out->felem_one = p384_felem_one;
out->point_dbl = p384_point_double;
Expand Down
27 changes: 24 additions & 3 deletions crypto/fipsmodule/ec/p521.c
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,27 @@ static const p521_limb_t p521_felem_p[P521_NLIMBS] = {

#endif // EC_NISTP_USE_S2N_BIGNUM

// The wrapper functions are needed for FIPS static build.
// Otherwise, initializing ec_nistp_meth with pointers to s2n-bignum
// functions directly generates :got: references that are also thought
// to be local_target by the delocator.
static inline void p521_felem_add_wrapper(ec_nistp_felem_limb *c,
const ec_nistp_felem_limb *a,
const ec_nistp_felem_limb *b) {
p521_felem_add(c, a, b);
}

static inline void p521_felem_sub_wrapper(ec_nistp_felem_limb *c,
const ec_nistp_felem_limb *a,
const ec_nistp_felem_limb *b) {
p521_felem_sub(c, a, b);
}

static inline void p521_felem_neg_wrapper(ec_nistp_felem_limb *c,
const ec_nistp_felem_limb *a) {
p521_felem_opp(c, a);
}

static p521_limb_t p521_felem_nz(const p521_limb_t in1[P521_NLIMBS]) {
p521_limb_t is_not_zero = 0;
for (int i = 0; i < P521_NLIMBS; i++) {
Expand Down Expand Up @@ -289,11 +310,11 @@ static void p521_point_add(p521_felem x3, p521_felem y3, p521_felem z3,
DEFINE_METHOD_FUNCTION(ec_nistp_meth, p521_methods) {
out->felem_num_limbs = P521_NLIMBS;
out->felem_num_bits = 521;
out->felem_add = bignum_add_p521;
out->felem_sub = bignum_sub_p521;
out->felem_add = p521_felem_add_wrapper;
out->felem_sub = p521_felem_sub_wrapper;
out->felem_mul = bignum_mul_p521_selector;
out->felem_sqr = bignum_sqr_p521_selector;
out->felem_neg = bignum_neg_p521;
out->felem_neg = p521_felem_neg_wrapper;
out->felem_nz = p521_felem_nz;
out->felem_one = p521_felem_one;
out->point_dbl = p521_point_double;
Expand Down
Loading