static bool sss_mc_is_valid_rec(struct sss_mc_ctx *mcc, struct sss_mc_rec *rec) { struct sss_mc_rec *self; uint32_t slot; if (((uint8_t *)rec < mcc->data_table) || ((uint8_t *)rec > (mcc->data_table + mcc->dt_size - MC_SLOT_SIZE))) { return false; } if ((rec->b1 == MC_INVALID_VAL) || (rec->b1 != rec->b2)) { return false; } if (!MC_CHECK_RECORD_LENGTH(mcc, rec)) { return false; } if (rec->expire == MC_INVALID_VAL64) { return false; } /* rec->next can be invalid if there are no next records */ if (rec->hash1 == MC_INVALID_VAL32) { return false; } else { self = NULL; slot = mcc->hash_table[rec->hash1]; while (slot != MC_INVALID_VAL32 && self != rec) { self = MC_SLOT_TO_PTR(mcc->data_table, slot, struct sss_mc_rec); slot = self->next; } if (self != rec) { return false; } } if (rec->hash2 != MC_INVALID_VAL32) { self = NULL; slot = mcc->hash_table[rec->hash2]; while (slot != MC_INVALID_VAL32 && self != rec) { self = MC_SLOT_TO_PTR(mcc->data_table, slot, struct sss_mc_rec); slot = self->next; } if (self != rec) { return false; } }
errno_t sss_nss_mc_get_record(struct sss_cli_mc_ctx *ctx, uint32_t slot, struct sss_mc_rec **_rec) { struct sss_mc_rec *rec; struct sss_mc_rec *copy_rec = NULL; size_t buf_size = 0; size_t rec_len; uint32_t b1; uint32_t b2; bool copy_ok; int count; int ret; /* try max 5 times */ for (count = 5; count > 0; count--) { rec = MC_SLOT_TO_PTR(ctx->data_table, slot, struct sss_mc_rec); /* fetch record length */ b1 = rec->b1; __sync_synchronize(); rec_len = rec->len; __sync_synchronize(); b2 = rec->b2; if (!MC_VALID_BARRIER(b1) || b1 != b2) { /* record is inconsistent, retry */ continue; } if (!MC_CHECK_RECORD_LENGTH(ctx, rec)) { /* record has invalid length */ free(copy_rec); return EINVAL; } if (rec_len > buf_size) { free(copy_rec); copy_rec = malloc(rec_len); if (!copy_rec) { ret = ENOMEM; goto done; } buf_size = rec_len; } /* we cannot access data directly, we must copy data and then * access the copy */ MEMCPY_WITH_BARRIERS(copy_ok, copy_rec, rec, rec_len); /* we must check data is consistent again after the copy */ if (copy_ok && b1 == copy_rec->b2) { /* record is consistent, use it */ break; } } if (count == 0) { /* couldn't successfully read header we have to give up */ ret = EIO; goto done; } *_rec = copy_rec; ret = 0; done: if (ret) { free(copy_rec); *_rec = NULL; } return ret; }